GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Artifact::isFollowupCommentDeleted()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 23
rs 9.0856
cc 3
eloc 17
nc 3
nop 1
1
<?php
2
/*
3
 * SourceForge: Breaking Down the Barriers to Open Source Development
4
 * Copyright 1999-2001 (c) VA Linux Systems
5
 * http://sourceforge.net
6
 *
7
 * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
8
 *
9
 * This file is a part of Codendi.
10
 *
11
 * Codendi is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * Codendi is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
/**
26
 *
27
 * Artifact.class.php - Main Artifact class
28
 */
29
class Artifact extends Error {
30
31
    const FORMAT_TEXT  = 0;
32
    const FORMAT_HTML  = 1;
33
    
34
    //The diffetents mode of display
35
    const OUTPUT_BROWSER      = 0;
36
    const OUTPUT_EXPORT       = 1;
37
    const OUTPUT_MAIL_TEXT    = 2;
38
39
    /**
40
     * Artifact Type object.
41
     *
42
     * @var             object  $ArtifactType.
43
     */
44
    var $ArtifactType; 
45
46
    /**
47
     * Array of artifact data.
48
     *
49
     * @var             array   $data_array.
50
     */
51
    var $data_array;
52
        
53
54
    /**
55
     *  Artifact - constructor.
56
     *
57
     *  @param  object  The ArtifactType object.
58
     *  @param  integer (primary key from database OR complete assoc array) 
59
     *          ONLY OPTIONAL WHEN YOU PLAN TO IMMEDIATELY CALL ->create()
60
     *  @return boolean success.
61
     */
62
    function Artifact(&$ArtifactType, $data=false, $checkPerms = true) {
63
      global $Language;
64
        $this->Error(); 
65
66
        $this->ArtifactType = $ArtifactType;
67
68
        //was ArtifactType legit?
69
        if (!$ArtifactType || !is_object($ArtifactType)) {
70
            $this->setError('Artifact: '.$Language->getText('tracker_common_canned','not_valid'));
71
            return false;
72
        }
73
        //did ArtifactType have an error?
74
        if ($ArtifactType->isError()) {
75
            $this->setError('Artifact: '.$ArtifactType->getErrorMessage());
76
            return false;
77
        }
78
                
79
        //
80
        //      make sure this person has permission to view artifacts belonging to this tracker
81
        //
82
        if ($checkPerms && !$this->ArtifactType->userCanView()) {
83
            $this->setError('Artifact: '.$Language->getText('tracker_common_artifact','view_private'));
84
            return false;
85
        }
86
87
        //
88
        //      set up data structures
89
        //
90
        if ($data) {
91
            if (is_array($data)) {
92
                $this->data_array = $data;
93
                //
94
                //      Should verify ArtifactType ID
95
                //
96
            } else {
97
                if (!$this->fetchData($data)) {
98
                    return false;
99
                }
100
            }
101
            //
102
            //      make sure this person has permission to view this artifact
103
            //
104
            if ($checkPerms) {
105
                if (!$this->userCanView()) {
106
                    $this->setError('Artifact: '.$Language->getText('tracker_common_artifact','view_private_artifact'));
107
                    return false;
108
                }
109
            }
110
        }
111
        return true;
112
    }
113
114
115
    /**
116
     *  fetchData - re-fetch the data for this Artifact from the database.
117
     *
118
     *  @param  int             The artifact ID.
119
     *  @return boolean success.
120
     */
121
    function fetchData($artifact_id) {
122
123
        global $art_field_fact,$Language;
124
        if (!$art_field_fact) {
125
            $art_field_fact = new ArtifactFieldFactory($this->ArtifactType);
126
        }
127
128
        // first fetch values of standard fields
129
        $sql = "SELECT * FROM artifact WHERE artifact_id='". db_ei($artifact_id) ."' AND group_artifact_id='". db_ei($this->ArtifactType->getID()) ."'";
130
        $res=db_query($sql);
131
        if (!$res || db_numrows($res) < 1) {
132
            $this->setError('Artifact: '.$Language->getText('tracker_common_artifact','invalid_id'));
133
            return false;
134
        }
135
        $this->data_array = db_fetch_array($res);
136
        db_free_result($res);
137
            
138
139
        // now get the values for generic fields if any
140
        $sql = "SELECT * FROM artifact_field_value WHERE artifact_id='". db_ei($artifact_id) ."'";
141
        $res=db_query($sql);
142
        if (!$res || db_numrows($res) < 1) {
143
            // if no result then it is possible that there isn't any generic fields
144
            return true;
145
        }
146
        while ($row = db_fetch_array($res)) {
147
            $data_fields[$row['field_id']] = $row;
148
        }
149
150
        // Get the list of all fields used by this tracker and append
151
        // the values for these generic fields to data_array
152
        $fields = $art_field_fact->getAllUsedFields();
153
154
        while (list($key,$field) = each($fields) ) {
0 ignored issues
show
Unused Code introduced by
The assignment to $key is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
155
            //echo $field->getName()."-".$field->getID()."<br>";
156
            // Skip! Standard field values fectched in previous query
157
            // and comment_type_id is not stored in artifact_field_value table
158
            if ( $field->isStandardField() ||
159
                 $field->getName() == "comment_type_id") {
160
                continue;
161
            }
162
            $this->data_array[$field->getName()] = $data_fields[$field->getID()][$field->getValueFieldName()];
163
164
        }
165
166
        return true;
167
    }
168
169
    /**
170
     *  getArtifactType - get the ArtifactType Object this Artifact is associated with.
171
     *
172
     *  @return object  ArtifactType.
173
     */
174
    function getArtifactType() {
175
        return $this->ArtifactType;
176
    }
177
        
178
    /**
179
     *  getValue - get the value for this artifact field.
180
     *
181
     *           @param name: the field name
182
     *  @return value
183
     */
184
    function getValue($name) {
185
        if (array_key_exists($name, $this->data_array)) return $this->data_array[$name];
186
    }
187
188
189
    /**
190
     *  getMultiAssignedTo - get the value for the 'multi_assigned_to' field
191
     *  This function is needed because getValue() won't return an array.
192
     *
193
     *  @return array
194
     */
195
    function getMultiAssignedTo() {
196
        $aid=$this->getID();
197
        if (!$aid) return;
198
        $sql="SELECT afv.valueInt 
199
              FROM artifact_field_value afv, artifact a, artifact_field af 
200
              WHERE a.artifact_id=". db_ei($aid) ." 
201
                AND afv.artifact_id=". db_ei($aid) ." 
202
                AND a.group_artifact_id=af.group_artifact_id 
203
                AND afv.field_id=af.field_id 
204
                AND af.field_name='multi_assigned_to'";
205
        $res=db_query($sql);
206
        $i=0;
207
        $return_val = array();
208
        while($resrow = db_fetch_array($res)) {
209
            $return_val[$i++]=$resrow['valueInt'];
210
        }
211
        return $return_val;
212
    }
213
214
    /**
215
     *  getID - get this ArtifactID.
216
     *
217
     *  @return int     The artifact_id #.
218
     */
219
    function getID() {
220
        return $this->data_array['artifact_id'];
221
    }
222
    
223
    /**
224
     * useArtifactPermissions
225
     * @return bool true if the artifact has individual permissions set
226
     */
227
    function useArtifactPermissions() {
228
        return $this->data_array['use_artifact_permissions'];
229
    }
230
    
231
    /**
232
     *  getStatusID - get open/closed/deleted flag.
233
     *
234
     *  @return int     Status: (1) Open, (2) Closed, (3) Deleted.
235
     */
236
    function getStatusID() {
237
        return $this->data_array['status_id'];
238
    }
239
240
    /**
241
     *  getSubmittedBy - get ID of submitter.
242
     *
243
     *  @return int user_id of submitter.
244
     */
245
    function getSubmittedBy() {
246
        return $this->data_array['submitted_by'];
247
    }
248
249
    /**
250
     *  getOpenDate - get unix time of creation.
251
     *
252
     *  @return int unix time.
253
     */
254
    function getOpenDate() {
255
        return $this->data_array['open_date'];
256
    }
257
258
     /**
259
     *  getLastUpdateDate - get unix time of last artifact update.
260
     *
261
     *  @return int unix time.
262
     */
263
    function getLastUpdateDate() {
264
        return $this->data_array['last_update_date'];
265
    }
266
267
   /**
268
     *  getCloseDate - get unix time of closure.
269
     *
270
     *  @return int unix time.
271
     */
272
    function getCloseDate() {
273
        return $this->data_array['close_date'];
274
    }
275
276
    /**
277
     *  getSummary - get text summary of artifact.
278
     *
279
     *  @return string The summary (subject).
280
     */
281
    function getSummary() {
282
        return $this->data_array['summary'];
283
    }
284
285
    /**
286
     *  getDetails - get text body (message) of artifact.
287
     *
288
     *  @return string  The body (message).
289
     */
290
    function getDetails() {
291
        return $this->data_array['details'];
292
    }
293
294
    /**
295
     *  getSeverity - get the severity of this artifact
296
     *
297
     *  @return int
298
     */
299
    function getSeverity() {
300
        return $this->data_array['severity'];
301
    }
302
303
    /**
304
     *  Insert an entry into the artifact_history
305
     *
306
     *  @param field: the field object
307
     *  @param old_value: the previous value of the field
308
     *  @param new_value: the current value of the field	
309
     *  @param type: extra information used to store the 'comment_type_id' field value (for the follow up comments)
310
     *  @param email: the email is the user is not logged in
311
     *
312
     *  @return int : the artifact_history_id
313
     */
314
    function addHistory ($field,$old_value,$new_value,$type=false,$email=false,$ahid=false,$comment_format=self::FORMAT_TEXT) {
315
    	//MLS: add case where we add CC and file_attachment into history for task #240
316
    	if (!is_object($field)) {
317
    		// "cc", "attachment", "comment", etc 
318
    		$name = $field;
319
    	} else {
320
    		// If field is not to be kept in bug change history then do nothing
321
    		if (!$field->getGlobalKeepHistory()) { return; }
322
    		$name = $field->getName();
323
    	}
324
325
    	/*
326
          handle the insertion of history for these parameters
327
        */
328
        if ($email) {
329
            // We use the email to identify the user
330
            $user=100;
331
        } else {
332
            if ( user_isloggedin() ) {
333
                $user=user_getid();
334
            } else {
335
                $user = 100;
336
            }
337
            $email = "";
338
        }
339
        
340
        // If type has a value add it into the sql statement (this is only for
341
        // the follow up comments (comment field))
342
        $fld_type = '';
343
        $val_type = '';
344
        if ($type) {
345
            $fld_type = ',type'; $val_type = ",'". db_ei($type) ."'";
346
        } else {
347
            // No comment type specified for a followup comment
348
            // so force it to None (100)
349
            if ($name == 'comment' || (preg_match("/^(lbl_)/",$name) && preg_match("/(_comment)$/",$name))) {
350
                $fld_type = ',type'; 
351
                $val_type = ",'100'";
352
            }
353
        }
354
355
        // Follow-up comments might have a different format
356
        if ($comment_format != self::FORMAT_TEXT && user_isloggedin()) {
357
            $fld_type .= ',format';
358
            $val_type .= ','.db_ei($comment_format);
359
        }
360
361
        $sql="insert into artifact_history(artifact_id,field_name,old_value,new_value,mod_by,email,date $fld_type) ".
362
            "VALUES (". db_ei($this->getID()) .",'". db_es($name) ."','". db_es($old_value) ."','". db_es($new_value) ."','". db_ei($user) ."','". db_es($email) ."','".time()."' $val_type)";
363
        //echo $sql;
364
        return db_query($sql);
365
    }
366
        
367
        
368
    /**
369
     *  Create a new artifact (and its values) in the db
370
     *
371
     * @param array $vfl the value-field-list. Array association pair of field_name => field_value. 
372
     *              If the function is called by the web-site submission form, the $vfl is set to false, and will be filled by the function extractFieldList function retrieving the HTTP parameters.
373
     *              If $vfl is not false, the fields expected in this array are *all* the fields of this tracker that are allowed to be submited by the user.
374
     *  @return boolean
375
     */
376
    function create($vfl=false,$import=false,$row=0) {
377
        global $ath,$art_field_fact,$Language;
378
        
379
        $group = $ath->getGroup();
380
        $group_artifact_id = $ath->getID();
381
        $error_message = ($import ? $Language->getText('tracker_common_artifact','row',$row) : "");
382
        
383
        // Retrieve HTTP GET variables and store them in $vfl array
384
        if (!$vfl) {
385
            $vfl = $art_field_fact->extractFieldList();
386
        }
387
        
388
        // We check the submitted fields to see if the user has the permissions to submit it
389
        if (!$import) {
390
            while ( list($key, $val) = each($vfl)) {
391
                $field = $art_field_fact->getFieldFromName($key);
392
                if ($field && (!$field->getName() == 'comment_type_id')) {   // SR #684 we don't check the perms for the field comment type
393
                    if (! $field->userCanSubmit($group->getID(),$group_artifact_id,user_getid())) {
394
                        // The user does not have the permissions to update the current field,
395
                        // we exit the function with an error message
396
                        $this->setError($Language->getText('tracker_common_artifact','bad_field_permission_submission', $field->getLabel()));
397
                        return false;
398
                    }
399
                    // we check if the given value is authorized for this field (for select box fields only)
400
                    // we don't check here the none value, we check after it with the function checkEmptyFields, to get a better error message if the field required (instead of value 100 is not a valid valid value for the field)
401
                    if ($field->isSelectBox() && $val != 100 && ! $field->checkValueInPredefinedValues($this->ArtifactType->getID(), $val)) {
402
                        $this->setError($Language->getText('tracker_common_artifact','bad_field_value', array($field->getLabel(), $val)));
403
                        return false;
404
                    }                    
405
                    if ($field->isMultiSelectBox()) {
406
                        foreach ($val as $a_value) {
407
                            if ($a_value != 100 && ! $field->checkValueInPredefinedValues($this->ArtifactType->getID(), $a_value)) {
408
                                $this->setError($Language->getText('tracker_common_artifact','bad_field_value', array($field->getLabel(), $val)));
409
                                return false;
410
                            }
411
                        }
412
                    }
413
                }
414
            }
415
            //When user is not autorised to submit some fields
416
            //we should block those artifact with mandatory fields and default value set to "None" 
417
            $fieldsNotShown = $art_field_fact->getAllFieldsNotShownOnAdd();
418
            if ($art_field_fact->checkEmptyFields($fieldsNotShown, false) == false) {
419
                $this->setError($Language->getText('tracker_common_artifact','mandatory_not_set'));
420
                return false;
421
            } 
422
        }
423
        
424
        if (!$import) {
425
            // make sure  required fields are not empty
426
            if ( $art_field_fact->checkEmptyFields($vfl) == false ) {
427
                $this->setError($art_field_fact->getErrorMessage());
428
                exit_missing_param();
429
            }
430
        }
431
        
432
        
433
        // we don't force them to be logged in to submit a bug
434
        if (!user_isloggedin()) {
435
            $user=100;
436
        } else {
437
            $user=user_getid();
438
        }
439
        
440
        
441
        // add default values for fields that have not been shown
442
        $add_fields = $art_field_fact->getAllFieldsNotShownOnAdd();
443
        while (list($key,$def_val) = each($add_fields)) {
444
            if (!array_key_exists($key,$vfl)) $vfl[$key] = $def_val;
445
        }
446
        
447
        
448
        if ($import &&
449
            $vfl['submitted_by'] &&
450
            $vfl['submitted_by'] != "")
451
        $user = $vfl['submitted_by'];
452
        
453
        
454
        // first make sure this wasn't double-submitted
455
        $field = $art_field_fact->getFieldFromName('summary');
456
        if ( $field && $field->isUsed()) {
457
            $res=db_query("SELECT * 
458
                FROM artifact 
459
                WHERE group_artifact_id = ". db_ei($ath->getID()) ." 
460
                AND submitted_by=".  db_ei($user) ." 
461
                AND summary='". db_es(htmlspecialchars($vfl['summary'])) ."'");
462
            if ($res && db_numrows($res) > 0) {
463
                $this->setError($Language->getText('tracker_common_artifact','double_subm',db_result($res,0,'artifact_id')));
464
                return false;           
465
            }
466
        }
467
        
468
        
469
        //
470
        //  Create the insert statement for standard field
471
        //
472
        //Reference manager for cross reference
473
        $reference_manager =& ReferenceManager::instance();
474
        reset($vfl);
475
        $vfl_cols = '';
476
        $vfl_values = '';
477
        $text_value_list=array();
478
        while (list($field_name,$value) = each($vfl)) {
479
            
480
            //echo "<br>field_name=$field_name, value=$value";
481
            
482
            $field = $art_field_fact->getFieldFromName($field_name);
483
            if ( $field && $field->isStandardField() ) {
484
                // skip over special fields  
485
                if ($field->isSpecial()) {
486
                    continue; 
487
                }
488
                
489
                $vfl_cols .= ','.$field->getName();
490
                $is_text = ($field->isTextField() || $field->isTextArea());
491
                if  ($is_text) {
492
                    $value = htmlspecialchars($value);
493
                    //Log for Cross references
494
                    $text_value_list[]=$value;
495
                    
496
                } else if ($field->isDateField()) {
497
                    // if it's a date we must convert the format to unix time
498
                    list($value,$ok) = util_date_to_unixtime($value);
0 ignored issues
show
Unused Code introduced by
The assignment to $ok is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
499
                }
500
                
501
                $vfl_values .= ',\''. db_es($value) .'\'';
502
                
503
            }
504
            
505
        } // while
506
        
507
        
508
        // Add all special fields that were not handled in the previous block
509
        $fixed_cols = 'open_date,last_update_date,group_artifact_id,submitted_by';
510
        if ($import) {
511
            if (!isset($vfl['open_date']) || !$vfl['open_date'] || $vfl['open_date'] == "") $open_date = time();
512
            else list($open_date,$ok) = util_date_to_unixtime($vfl['open_date']);
0 ignored issues
show
Unused Code introduced by
The assignment to $ok is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
513
            $fixed_values = "'". db_ei($open_date) ."','".time()."','". db_ei($group_artifact_id) ."','". db_ei($user) ."'";
514
        } else {
515
            $fixed_values = "'".time()."','".time()."','". db_ei($group_artifact_id) ."','". db_ei($user) ."'";
516
        }  
517
        
518
        
519
        //
520
        //  Finally, build the full SQL query and insert the artifact itself 
521
        //
522
        $id_sharing = new TrackerIdSharingDao();
523
        if ($artifact_id = $id_sharing->generateArtifactId()) {
524
            $sql="INSERT INTO artifact (artifact_id, $fixed_cols $vfl_cols) VALUES ($artifact_id, $fixed_values $vfl_values)";
525
            //echo "<br>DBG - SQL insert artifact: $sql";
526
            $result=db_query($sql);
527
            
528
            $was_error = false;
529
            if (!$result || db_affected_rows($result) == 0) {
530
                $this->setError($Language->getText('tracker_common_artifact','insert_err',$sql));
531
                $was_error = true;
532
            } else {
533
                
534
                //
535
                //  Insert the field values for no standard field
536
                //
537
                $fields = $art_field_fact->getAllUsedFields();
538
                while (list($field_name,$field) = each($fields)) {
539
                    
540
                    // skip over special fields  
541
                    if ( ($field->isSpecial())||($field->isStandardField()) ) {
542
                        continue; 
543
                    }
544
                    
545
                    if ( array_key_exists($field_name, $vfl) && isset($vfl[$field_name]) && $vfl[$field_name] ) {
546
                        // The field has a value from the user input
547
                        
548
                        $value = $vfl[$field_name];
549
                        
550
                        $is_text = ($field->isTextField() || $field->isTextArea());
551
                        if  ($is_text) {
552
                            $value = htmlspecialchars($value);
553
                            
554
                            //Log for Cross references
555
                            $text_value_list[]=$value;
556
                            
557
                            
558
                            
559
                        } else if ($field->isDateField()) {
560
                            // if it's a date we must convert the format to unix time
561
                            list($value,$ok) = util_date_to_unixtime($value);
0 ignored issues
show
Unused Code introduced by
The assignment to $ok is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
562
                        }
563
                        
564
                        // Insert the field value
565
                        if ( !$field->insertValue($artifact_id,$value) ) {
566
                            $error_message .= $Language->getText('tracker_common_artifact','field_err',array($field->getLabel(),$value));
567
                            $was_error = true;
568
                            $this->setError($error_message);
569
                        }
570
                        
571
                    } else {
572
                        // The field hasn't a value from the user input
573
                        // We need to insert default value for this field
574
                        // because all SQL queries (from Report or Artifact read/update) don't allow 
575
                        // empty record (we must use join and not left join for performance reasons).
576
                        
577
                        if ( !$field->insertValue($artifact_id,$field->getDefaultValue()) ) {
578
                            $error_message .= $Language->getText('tracker_common_artifact','def_err',array($field->getLabel(),$field->getDefaultValue()));
579
                            $was_error = true;
580
                            $this->setError($error_message);
581
                        }
582
                        
583
                    }
584
                    
585
                } // while
586
                
587
                
588
            }
589
            
590
            //Add Cross Reference
591
            for($i=0;$i<sizeof($text_value_list);$i++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
592
                $reference_manager->extractCrossRef($text_value_list[$i],$artifact_id,ReferenceManager::REFERENCE_NATURE_ARTIFACT,$ath->getGroupID());
0 ignored issues
show
Bug introduced by
It seems like $artifact_id defined by $id_sharing->generateArtifactId() on line 523 can also be of type boolean; however, ReferenceManager::extractCrossRef() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
593
            }
594
            
595
            // artifact permissions
596
            $request = HTTPRequest::instance();
597
            $this->data_array['artifact_id'] = $artifact_id; // cheat
598
            $this->setPermissions($request->get('use_artifact_permissions_name'), $request->get('ugroups'));
599
            
600
            // All ok then reload the artifact data to make sure it is cached
601
            // correctly in memory
602
            $this->fetchData($artifact_id);
603
        } else {
604
            $this->setError($Language->getText('tracker_common_artifact','insert_err',$sql));
0 ignored issues
show
Bug introduced by
The variable $sql seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
605
            $was_error = true;
606
        }
607
        return !$was_error;
608
    }
609
610
611
612
    /**
613
     *  Add a followup comment
614
     *
615
     * @param comment: the comment
616
     * @param email: user email if the user is not logged in
617
     * @param changes (OUT): array of changes (for notifications)
618
     *
619
     *  @return boolean
620
     */
621
    function addComment($comment,$email=false,&$changes,$comment_format=self::FORMAT_TEXT) {
622
                        
623
        global $art_field_fact,$Language;
624
625
        // Add a new comment if there is one
626
        if ($comment != '') {
627
                
628
            // For none project members force the comment type to None (100)
629
            if ( !user_isloggedin() ) {
630
                if ( $email ) {
631
                    $this->addHistory('comment', "", htmlspecialchars($comment), 100, $email, null, $comment_format);
632
                } else {
633
                    $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','enter_email'));
634
                    return false;
635
                }
636
            } else {
637
                $this->addHistory('comment', "", htmlspecialchars($comment), 100, null, null, $comment_format);
638
            }
639
            $changes['comment']['add'] = stripslashes($comment);
640
            $changes['comment']['type'] = $Language->getText('global','none');
641
                        
642
            $GLOBALS['Response']->addFeedback('info', $Language->getText('tracker_common_artifact','add_comment'));               
643
            return true;
644
        } else {
645
            return false;
646
        }
647
    }
648
649
650
    /** 
651
     * handle a simple follow-up comment
652
     * Followup comments are added in the bug history along with the comment type.
653
     * 
654
     * If a canned response is given it overrides anything typed in the followup
655
     * comment text area
656
     *
657
     * @param comment (IN) : the comment that the user typed in
658
     * @param canned_response (IN) : the id of the canned response
659
     */
660
    function addFollowUpComment($comment,$comment_type_id,$canned_response,&$changes,$comment_format=self::FORMAT_TEXT) {
661
    	global $art_field_fact,$Language;
662
      	if ($canned_response && $canned_response != 100) {
663
	
664
			$sql="SELECT * FROM artifact_canned_responses WHERE artifact_canned_id='". db_ei($canned_response) ."'";
665
			$res3=db_query($sql);
666
			
667
			if ($res3 && db_numrows($res3) > 0) {
668
			  $comment = util_unconvert_htmlspecialchars(db_result($res3,0,'body'));
669
			  $GLOBALS['Response']->addFeedback('info', $Language->getText('tracker_common_artifact','canned_used'));
670
			} else {
671
			  $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','unable_canned'));
672
			  $GLOBALS['Response']->addFeedback('error', db_error());
673
			}
674
      	}
675
      
676
      	if ($comment != '') {
677
        	$this->addHistory('comment', '', htmlspecialchars($comment), $comment_type_id, false, false, $comment_format);
678
            $changes['comment']['add'] = $comment;
679
            $changes['comment']['format'] = $comment_format;
680
681
			$field = $art_field_fact->getFieldFromName("comment_type_id");
682
			if ( $field && isset($comment_type_id) && $comment_type_id) {
683
			  $changes['comment']['type'] =
684
			    $field->getValue($this->ArtifactType->getID(), $comment_type_id);
685
			} 
686
			$reference_manager = $this->getReferenceManager();
687
        	$reference_manager->extractCrossRef($comment,$this->getID(), ReferenceManager::REFERENCE_NATURE_ARTIFACT, $this->ArtifactType->getGroupID());    
688
			
689
    		return true;
690
      	} else {
691
        	return false;
692
      	}
693
    }
694
695
    /**
696
     * Wrapper for tests
697
     *
698
     * @return ReferenceManager
699
     */
700
    function getReferenceManager() {
701
        return ReferenceManager::instance();
702
    }
703
704
    /**
705
     *  Add a list of follow-up comments coming from the import facility
706
     *
707
     * @param parsed_comments (IN): an array (#detail => array2), where array2 is of the form
708
     *                              ("date" => date, "by" => user, "type" => comment-type, "comment" => comment-string)
709
     *  @return boolean
710
     */
711
    function addFollowUpComments($parsed_comments) {
712
    	global $Language;
713
714
        $art_field_fact = new ArtifactFieldFactory($this->ArtifactType);
715
        $artifact_import = new ArtifactImport($this->ArtifactType, $art_field_fact, $this->ArtifactType->Group);
716
        
717
      	while (list(,$arr) = each($parsed_comments)) {        
718
		  	$by = $arr['by'];
719
			if ($by == "100") {
720
				//this case should not exist in new trackers but
721
			    //can appear if we parse legacy bugs or tasks
722
			    $email = $Language->getText('global','none');
723
			    $user_id = 100;
724
			} else if (user_getname($by)) {
725
				$user_id = $by;
726
				$email = "";
727
			} else {
728
				$email = $by;
729
				$user_id = 100;
730
			}	
731
            
732
            if ( ! $artifact_import->checkCommentExist($arr, $this->getID())) {
733
                if (!$artifact_import->checkCommentExistInLegacyFormat($arr, $this->getID())) {
734
                    $comment  = htmlspecialchars($arr['comment']);
735
                    $sql="insert into artifact_history(artifact_id,field_name,old_value,new_value,mod_by,email,date,type) ".
736
		    	         "VALUES (". db_ei($this->getID()) .",'comment','','". db_es($comment) ."','". db_ei($user_id) ."','". db_es($email) ."','". db_ei($arr['date']) ."','". db_ei($arr['type']) ."')";
737
738
                    db_query($sql);
739
                }
740
            }
741
742
		}
743
		
744
		
745
		return true;
746
    }
747
        
748
    /**
749
    * Update a follow-up comment
750
    * 
751
    * @param comment_id: follow-up comment id
752
    * @param comment_txt: text of the follow-up comment
753
    * 
754
    * @return boolean
755
    */
756
    function updateFollowupComment($comment_id,$comment_txt,&$changes, $comment_format=self::FORMAT_TEXT) {
757
        if ($this->userCanEditFollowupComment($comment_id)) {
758
            $sql = 'SELECT field_name, new_value, type FROM artifact_history'
759
                .' WHERE artifact_id='. db_ei($this->getID()) 
760
                .' AND artifact_history_id='. db_ei($comment_id) 
761
                .' AND (field_name="comment" OR field_name LIKE "lbl_%_comment")';
762
            $qry = db_query($sql);
763
            $new_value = db_result($qry,0,'new_value');
764
            $comment_type_id = db_result($qry,0,'type');
765
            if ($new_value == $comment_txt) {
766
                //comment doesn't change
767
                return false;
768
            }
769
    
770
            if ($qry) {
771
                $fname = db_result($qry,0,'field_name');
772
                if (preg_match("/^(lbl_)/",$fname) && preg_match("/(_comment)$/",$fname)) {
773
                    $comment_lbl = $fname;
774
                } else {
775
                    $comment_lbl = "lbl_".$comment_id."_comment";    
776
                }
777
                //now add new comment entry
778
                $this->addHistory($comment_lbl,$new_value,htmlspecialchars($comment_txt), $comment_type_id,false,$comment_id,$comment_format);
779
                $changes['comment']['del'] = $new_value;
780
                $changes['comment']['add'] = $comment_txt;
781
                $changes['comment']['format'] = $comment_format;
782
                $reference_manager = $this->getReferenceManager();
783
        		$reference_manager->extractCrossRef($comment_txt,$this->getID(),ReferenceManager::REFERENCE_NATURE_ARTIFACT,$this->ArtifactType->getGroupID());    
784
                
785
                return true;
786
            } else {
787
                return false;
788
            }
789
        } else {
790
            $this->setError($GLOBALS['Language']->getText('tracker_common_artifact','err_upd_comment', array($comment_id, $GLOBALS['Language']->getText('include_exit','perm_denied'))));
791
            $GLOBALS['Response']->addFeedback('error',$GLOBALS['Language']->getText('tracker_common_artifact','err_upd_comment', array($comment_id, $GLOBALS['Language']->getText('include_exit','perm_denied'))));
792
            return false;
793
        }
794
795
    }
796
    
797
    /**
798
     *  Update an artifact. Rk: vfl is an variable list of fields, Vary from one project to another
799
     *  return true if artifact updated, false if nothing changed or DB update failed
800
     *
801
     * @param artifact_id_dependent: artifact dependencies
802
     * @param canned_response: canned responses
803
     * @param changes (OUT): array of changes (for notifications)
804
     *
805
     *  @return boolean
806
     */
807
    function handleUpdate ($artifact_id_dependent,$canned_response,&$changes,$masschange=false,$vfl=false,$import=false){
808
    	global $art_field_fact,$HTTP_POST_VARS,$Language;
809
	    if ($masschange && !$this->ArtifactType->userIsAdmin()) exit_permission_denied();
810
        
811
	    if (!$import) {
812
	    	// Retrieve HTTP GET variables and store them in $vfl array
813
        	$vfl = $art_field_fact->extractFieldList();
814
815
	        // make sure  required fields are not empty
816
        	if ( ($art_field_fact->checkEmptyFields($vfl) == false) ) {
817
                	exit_missing_param();
818
            	}
819
	    }
820
        
821
        //get this artifact from the db
822
        $result=$this->getFieldsValues();
823
    
824
            
825
                
826
        //
827
        //  See which fields changed during the modification
828
        //  and if we must keep history then do it. Also add them to the update
829
        //  statement
830
        //
831
        $reference_manager =& ReferenceManager::instance();
832
        $text_value_list=array();
833
        $changes = array();
834
        $upd_list = '';
835
        reset($vfl);
836
        while (list($field_name,$value) = each($vfl)) {
837
                
838
            $field = $art_field_fact->getFieldFromName($field_name);
839
                
840
            // skip over special fields  except for details which in this 
841
            // particular case can be processed normally
842
            if ($field->isSpecial()) {
843
	            continue; 
844
            }
845
                    
846
            if ($field->isInt() && $value == '' && $field->getRequired()== 0) {
847
                $value = 0;
848
            }
849
            // we check if the given value is authorized for this field (for select box fields only)
850
            // we don't check here the none value, we have already check it before (we can't check here the none value because the function checkValueInPredefinedValues don't take the none value into account)
851
            // if the value did not change, we don't do the check (because of stored values that can be deleted now)
852
            if (! $masschange && $result[$field_name] != $value && $field->isSelectBox() && $value != 100 && ! $field->checkValueInPredefinedValues($this->ArtifactType->getID(), $value)) {
853
                $this->setError($Language->getText('tracker_common_artifact','bad_field_value', array($field->getLabel(), $value)));
854
                return false;
855
            }                    
856
            if (! $masschange && $field->isMultiSelectBox()) {
857
                if (is_array($value)) {
858
                    foreach ($value as $a_value) {
859
                        if ($a_value != 100 && ! $field->checkValueInPredefinedValues($this->ArtifactType->getID(), $a_value)) {
860
                            $this->setError($Language->getText('tracker_common_artifact','bad_field_value', array($field->getLabel(), $value)));
861
                            return false;
862
                        }
863
                    }
864
                }
865
            }
866
            
867
            $is_text = ($field->isTextField() || $field->isTextArea());
868
            if ( ($field->isMultiSelectBox())&&(is_array($value)) ) {
869
870
				if ($masschange && (in_array($Language->getText('global','unchanged'),$value))) {
871
					continue;
872
				}
873
	            // The field is a multi values field and it has multi assigned values
874
	            $values = $value;
875
	            
876
		        // check if the user can update the field or not
877
		        if (! $field->userCanUpdate($this->ArtifactType->getGroupID(), $this->ArtifactType->getID(), user_getid())) {
878
		            // we only throw an error if the values has changed
879
		            $old_values = $field->getValues($this->getID());
880
		            list($deleted_values,$added_values) = util_double_diff_array($old_values,$values);
881
		            if ((count($deleted_values) > 0) || (count($added_values) > 0)) {
882
		                // The user does not have the permissions to update the current field,
883
		                // we exit the function with an error message
884
		                $this->setError($Language->getText('tracker_common_artifact','bad_field_permission_update', $field->getLabel()));
885
	                    return false;
886
	                }
887
            	}
888
889
				//don't take into account the none value if there are several values selected
890
				if (count($values) > 1) {
891
					$temp = array();
892
					while (list($i,$v) = each($values)) {
893
						if ($v == 100) {
894
							unset($values[$i]);
895
							$unset = true;
896
						} else {
897
							$temp[] = $v;
898
						}
899
					}
900
					if (isset($unset) && $unset) $values = $temp;
901
				}
902
903
           		$old_values = $field->getValues($this->getID());
904
            
905
	            list($deleted_values,$added_values) = util_double_diff_array($old_values,$values);
906
907
	            // Check if there are some differences
908
	            if ((count($deleted_values) > 0) || (count($added_values) > 0)) {
909
	
910
	                // Add values in the history
911
	                $a = $field->getLabelValues($this->ArtifactType->getID(),$old_values);
912
	                $val = join(",",$a);
913
				    $b = $field->getLabelValues($this->ArtifactType->getID(),$values);
914
				    $new_val = join(",",$b);
915
	                $this->addHistory($field,$val,$new_val);
916
	                                
917
	                // Update the field value
918
	                if ( !$field->updateValues($this->getID(),$values) ) {
919
	                    $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','field_upd_fail',$field->getLabel()));
920
	                }
921
	                if ($is_text) {
922
                        //Log for Cross references
923
                        $text_value_list[]=$values;
924
                    }
925
                                        
926
	                // Keep track of the change
927
	                $field_html = new ArtifactFieldHtml($field);
928
	                if (count($deleted_values) > 0) {
929
	                    $val = join(",",$field->getLabelValues($this->ArtifactType->getID(),$deleted_values));
930
	                    $changes[$field_name]['del']=$val;
931
	                }
932
	                if (count($added_values) > 0) {
933
	                    $val = join(",",$field->getLabelValues($this->ArtifactType->getID(),$added_values));
934
	                    $changes[$field_name]['add']=$val;
935
	                }
936
	            }
937
                                        
938
	        } else {
939
	        	if ($masschange && ($value==$Language->getText('global','unchanged'))) {
940
					continue;
941
				}	
942
       
943
                $old_value = $result[$field_name];
944
                if  ($is_text) {
945
                    $differ = ($old_value != htmlspecialchars($value));
946
                    //Log for Cross references
947
					$text_value_list[]=$value; 
948
					
949
                } else if ($field->isDateField()) {
950
                    // if it's a date we must convert the format to unix time
951
	 				if ($value != '') list($value,$ok) = util_date_to_unixtime($value);
952
			  		else $value = '0';
953
954
				    //first have a look if both dates are uninitialized
955
					if (($old_value == 0 || $old_value == '') && ($value == 0 || !$ok )) {
956
						$differ = false;
957
					} else {
958
				    	// and make also sure that the old_value has been treated as the new value
959
				    	// i.e. old_value (unix timestamp) -> local date (with hours cut off, so change the date by x  hours) -> unixtime
960
						$old_date = format_date("Y-m-j",$old_value);
961
						list($old_val,$ok) = util_date_to_unixtime($old_date);
962
                    	$differ = ($old_val != $value);
963
			    	}
964
                } else {
965
	                $differ = ($old_value != $value);
966
                }
967
                if ($differ) {
968
	                // The userCanUpdate test is only done on modified fields
969
                    if ( $field->userCanUpdate($this->ArtifactType->getGroupID(), $this->ArtifactType->getID(), user_getid())) {
970
                                
971
	                    if ($is_text) {
972
	                        if ( $field->isStandardField() ) {
973
	                            $upd_list .= "$field_name='".db_es(htmlspecialchars($value))."',";                                                 
974
                            } else {
975
                                $update_value = htmlspecialchars($value);
976
                            }
977
                                                    
978
                            $this->addHistory($field,$old_value,$value);
979
                            $value = stripslashes($value);
980
                        } else {
981
                            if ( $field->isStandardField() ) {
982
                                $upd_list .= "$field_name='". db_es($value) ."',";
983
                        	} else {
984
                            	$update_value = $value;
985
                        	}
986
                            $this->addHistory($field,$old_value,$value);
987
                        }
988
                                    
989
                        // Update the field value
990
                        if ( !$field->isStandardField() ) {
991
                            if ( !$field->updateValue($this->getID(),$update_value) ) {
992
                                $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','field_upd_fail',$field->getLabel()));
993
                            }
994
                        }
995
                                        
996
                        // Keep track of the change
997
                        $field_html = new ArtifactFieldHtml($field);
998
                        $changes[$field_name]['del']=$field_html->display($this->ArtifactType->getID(),$old_value,false,false,true,true);
999
                        $changes[$field_name]['add']=$field_html->display($this->ArtifactType->getID(),$value,false,false,true,true);
1000
                    } else {
1001
	                    // The user does not have the permissions to update the current field,
1002
	                    // we exit the function with an error message
1003
	                    $this->setError($Language->getText('tracker_common_artifact','bad_field_permission_update', $field->getLabel()));
1004
	                    return false;
1005
	                }
1006
	            }
1007
            }
1008
        } // while
1009
1010
		for($i=0;$i<sizeof($text_value_list);$i++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1011
			$reference_manager->extractCrossRef($text_value_list[$i],$this->getID(),ReferenceManager::REFERENCE_NATURE_ARTIFACT,$this->ArtifactType->getGroupID());
1012
		}
1013
1014
		$request = HTTPRequest::instance();
1015
	    //for masschange look at the special case of changing the submitted_by param
1016
	    if ($masschange) {
1017
			reset($HTTP_POST_VARS);
1018
			while ( list($key, $val) = each($HTTP_POST_VARS)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $val is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1019
	            $val = $request->get($key); //Don't use HTTP_POST_VARS
1020
				if ($key == 'submitted_by' && $val != $Language->getText('global','unchanged')) {
1021
					$sql = "UPDATE artifact SET submitted_by=". db_ei($val) ." WHERE artifact_id = ". db_ei($this->getID()) ;
1022
					$res = db_query($sql);
1023
					$field = $art_field_fact->getFieldFromName('submitted_by');
1024
					if ($this->getSubmittedBy() != $val)
1025
						$this->addHistory('submitted_by',$this->getSubmittedBy(),$val);
1026
				}
1027
			}
1028
	    }
1029
1030
        // Comment field history is handled a little differently. Followup comments
1031
        // are added in the bug history along with the comment type.
1032
        // 
1033
        // If a canned response is given it overrides anything typed in the followup
1034
        // comment text area. 
1035
        $comment = $request->get('comment');
1036
        $comment_type_id = array_key_exists('comment_type_id', $vfl)?$vfl['comment_type_id']:'';
1037
        $vFormat = new Valid_WhiteList('comment_format', array(self::FORMAT_HTML, self::FORMAT_TEXT));
1038
        $comment_format = $request->getValidated('comment_format', $vFormat, self::FORMAT_TEXT);
1039
1040
	    $this->addFollowUpComment($comment,$comment_type_id,$canned_response,$changes,$comment_format);
1041
            
1042
        //
1043
        //  Enter the timestamp if we are changing to closed or declined
1044
        //
1045
        if (isset($changes['status_id']) && $this->isStatusClosed($vfl['status_id'])) {
1046
            $now=time();
1047
            $upd_list .= "close_date='$now',";
1048
            $field = $art_field_fact->getFieldFromName('close_date');
1049
            if ( $field ) {
1050
                $this->addHistory ($field,$result['close_date'],'');
1051
            }
1052
        }
1053
        
1054
        //
1055
        //  Reset the timestamp if we are changing from closed or declined
1056
        //
1057
        if (isset($changes['status_id']) && !$this->isStatusClosed($vfl['status_id'])) {
1058
            $upd_list .= "close_date='',";
1059
            $field = $art_field_fact->getFieldFromName('close_date');
1060
            if ( $field ) {
1061
                $this->addHistory ($field,$result['close_date'],'');
1062
            }
1063
        }
1064
1065
        //
1066
        //  Insert the list of dependencies 
1067
        //
1068
        
1069
	    if ($import && $artifact_id_dependent) {
1070
			if (!$this->deleteAllDependencies()) {
1071
                return false;
1072
            }
1073
			if ($artifact_id_dependent == $Language->getText('global','none')) {
1074
                unset($artifact_id_dependent);
1075
            }
1076
	    }
1077
        if (isset($artifact_id_dependent)) {
1078
            if (!$this->addDependencies($artifact_id_dependent,$changes,$masschange, $import)) {
1079
                return false;
1080
            }
1081
        }
1082
                
1083
        //
1084
        //  Finally, build the full SQL query and update the artifact itself (if need be)
1085
        //
1086
    
1087
        $res_upd = true;
1088
        if ($upd_list) {
1089
            // strip the excess comma at the end of the update field list
1090
            $upd_list = substr($upd_list,0,-1);
1091
            
1092
            $sql="UPDATE artifact SET $upd_list ".
1093
                " WHERE artifact_id=". db_ei($this->getID()) ;
1094
            
1095
            $res_upd=db_query($sql);
1096
        }
1097
1098
        if (!$res_upd) {
1099
            exit_error($Language->getText('tracker_common_artifact','upd_fail').': '.$sql,$Language->getText('tracker_common_artifact','upd_fail'));
1100
            return false;
1101
        } else {
1102
            if (!$request->exist('change_permissions') || $request->get('change_permissions')) {
1103
                $this->setPermissions($request->get('use_artifact_permissions_name'), $request->get('ugroups'));
1104
            }
1105
            return true;
1106
        }
1107
1108
    }
1109
1110
    /**
1111
     * Set the permissions
1112
     */
1113
    function setPermissions($use_artifact_permissions, $ugroups) {
1114
        if ($this->ArtifactType->userIsAdmin()) {
1115
            if ($use_artifact_permissions) {
1116
                $result = permission_process_selection_form($this->ArtifactType->getGroupID(), 'TRACKER_ARTIFACT_ACCESS', $this->getId(), $ugroups);
0 ignored issues
show
Deprecated Code introduced by
The function permission_process_selection_form() has been deprecated.

This function has been deprecated.

Loading history...
1117
                if (!$result[0]) {
1118
                    return $GLOBALS['Response']->addFeedback('error', $result[1]);
1119
                }
1120
                //If the selected ugroup corresponds to the default one (all_user), there is no need to store it
1121
                if ($ugroups[0] == $GLOBALS['UGROUP_ANONYMOUS']) {
1122
                    $use_artifact_permissions = 0;
1123
                }
1124
            }
1125
            $sql = "UPDATE artifact 
1126
                    SET use_artifact_permissions = ". ($use_artifact_permissions ? 1 : 0) ."
1127
                    WHERE artifact_id=". db_ei($this->getID());
1128
            db_query($sql);
1129
            
1130
        }
1131
    }
1132
1133
1134
    /**
1135
     * Check if an email address already exists
1136
     *
1137
     * @param cc: the email address
1138
     *
1139
     * @return boolean
1140
     */
1141
    function existCC($cc) {
1142
        $sql = "SELECT artifact_cc_id FROM artifact_cc WHERE artifact_id=". db_ei($this->getID()) ." AND email='". db_es($cc) ."'";
1143
        $res = db_query($sql);
1144
        return (db_numrows($res) >= 1);
1145
    }
1146
1147
    /**
1148
     * Insert an email address for the CC list
1149
     *
1150
     * @param cc: the email address
1151
     * @param added_by: user who insert this cc list
1152
     * @param comment: comment for this cc list
1153
     * @param date: date of creation
1154
     *
1155
     * @return boolean
1156
     */
1157
    function insertCC($cc,$added_by,$comment,$date) {
1158
        $sql = "INSERT INTO artifact_cc (artifact_id,email,added_by,comment,date) ".
1159
            "VALUES (". db_ei($this->getID()) .",'". db_es($cc) ."','". db_ei($added_by) ."','". db_es($comment) ."','". db_ei($date) ."')";
1160
        $res = db_query($sql);
1161
        return ($res);
1162
        
1163
    }
1164
1165
    /**
1166
     * Insert email addresses for CC list
1167
     *
1168
     * @param email: list of email addresses
1169
     * @param comment: comment for these addresses
1170
     * @param changes (OUT): list of changes
1171
     * @param masschange: if in a masschange, we do not wan't to get feedback when everything ok
1172
     *
1173
     * @return boolean
1174
     */
1175
    function addCC($email,$comment,&$changes,$masschange=false) {
1176
        global $Language;
1177
        
1178
        $user_id = (user_isloggedin() ? user_getid(): 100);
1179
        
1180
        $arr_email = util_split_emails($email);
1181
        $date = time();
1182
        $ok = true;
1183
        $changed = false;
1184
        
1185
        if (! util_validateCCList($arr_email, $message)) {
1186
            exit_error($Language->getText('tracker_index','cc_list_invalid'), $message);
1187
        }
1188
	
1189
	//calculate old_values to put into artifact_history
1190
	$old_value=$this->getCCEmails();
1191
    reset($arr_email);
1192
        while (list(,$cc) = each($arr_email)) {
1193
            // Add this cc only if not there already
1194
            if (!$this->existCC($cc)) {
1195
                $changed = true;
1196
                $res = $this->insertCC($cc,$user_id,$comment,$date);
1197
                if (!$res) { $ok = false; } 
1198
            }
1199
        }
1200
	
1201
	if ($old_value == '') {
1202
            $new_value = join(',', $arr_email);
1203
	} else {
1204
	    $new_value = $old_value .",".join(',', $arr_email);
1205
	}
1206
1207
        if (!$ok) {
1208
            $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','cc_add_fail'));
1209
        } else {
1210
            $this->addHistory('cc',$old_value,$new_value);
1211
            $changes['CC']['add'] = join(',', $arr_email);
1212
        }
1213
        return $ok;
1214
    }
1215
1216
    /**
1217
     * Delete old cc list and add new email instead
1218
     *
1219
     * @param email: list of email addresses
1220
     * @param comment: comment for these addresses
1221
     *
1222
     * @return boolean
1223
     */
1224
    function updateCC($email,$comment) {
1225
        global $Language;
1226
        
1227
        $user_id = (user_isloggedin() ? user_getid(): 100);
1228
        
1229
        $arr_email = util_split_emails($email);
1230
        $date = time();
1231
        $ok = true;
1232
        $changed = false;
1233
        
1234
        if (! util_validateCCList($arr_email, $message)) {
1235
            exit_error($Language->getText('tracker_index','cc_list_invalid'), $message);
1236
        }
1237
	
1238
	//calculate old_values to put into artifact_history
1239
	$old_value=$this->getCCEmails();
1240
        $new_value = join(',', $arr_email);
1241
1242
	//look if there is really something to do or not
1243
	list($deleted_values,$added_values) = util_double_diff_array(explode(",",$old_value),$arr_email);
1244
	if (count($deleted_values) == 0 && count($added_values) == 0) return true;
1245
1246
	if (!$this->deleteAllCC()) {
1247
		$GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','prob_cc_list',$this->getID()));
1248
		$ok = false;
1249
	}
1250
1251
	reset($arr_email);
1252
	while (list(,$cc) = each($arr_email)) {
1253
                $changed = true;
1254
                $res = $this->insertCC($cc,$user_id,$comment,$date);
1255
                if (!$res) { $ok = false; } 
1256
        }
1257
1258
        if (!$ok) {
1259
            $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','cc_add_fail'));
1260
        } else {
1261
	    $this->addHistory('cc',$old_value,$new_value);
1262
        }
1263
        return $ok;
1264
    }
1265
1266
1267
1268
    /**
1269
     * Delete an email address in the CC list
1270
     *
1271
     * @param artifact_cc_id: cc list id
1272
     * @param changes (OUT): list of changes
1273
     *
1274
     * @return boolean
1275
     */
1276
    function deleteCC($artifact_cc_id=false,&$changes,$masschange=false) {
1277
        global $Language;
1278
        
1279
        // If both bug_id and bug_cc_id are given make sure the cc belongs 
1280
        // to this bug (it's a bit paranoid but...)
1281
        $sql = "SELECT artifact_id,email from artifact_cc WHERE artifact_cc_id='". db_ei($artifact_cc_id) ."'";
1282
        $res1 = db_query($sql);
1283
        if ((db_numrows($res1) <= 0) || (db_result($res1,0,'artifact_id') != $this->getID()) ) {
1284
            $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','err_cc_id',$artifact_cc_id));
1285
            return false;
1286
        }
1287
        	
1288
	//calculate old_values to put into artifact_history
1289
	$old_value=$this->getCCEmails();
1290
        
1291
	// Now delete the CC address
1292
        $res2 = db_query("DELETE FROM artifact_cc WHERE artifact_cc_id='". db_ei($artifact_cc_id) ."'");
1293
        if (!$res2) {
1294
            $GLOBALS['Response']->addFeedback('error', $Language->getText('tracker_common_artifact','err_del_cc',array($artifact_cc_id,db_error($res2))));
1295
            return false;
1296
        } else {
1297
            if (!$masschange) $GLOBALS['Response']->addFeedback('info', $Language->getText('tracker_common_artifact','cc_remove'));
1298
	    $new_value=$this->getCCEmails();
1299
	    $this->addHistory('cc',$old_value,$new_value);
1300
            $changes['CC']['del'] = db_result($res1,0,'email');
1301
            return true;
1302
        }
1303
    }
1304
1305
    /**
1306
     * Check if an artifact depends already from the current one
1307
     *
1308
     * @param id: the artifact id
1309
     *
1310
     * @return boolean
1311
     */
1312
    function existDependency($id) {
1313
        $sql = "SELECT is_dependent_on_artifact_id FROM artifact_dependencies WHERE artifact_id=". db_ei($this->getID()) ." AND is_dependent_on_artifact_id=". db_ei($id);
1314
        //echo $sql;
1315
        $res = db_query($sql);
1316
        return (db_numrows($res) >= 1);
1317
    }
1318
        
1319
    /**
1320
     * Check if an artifact exists
1321
     *
1322
     * @param id: the artifact id
1323
     *
1324
     * @return boolean
1325
     */
1326
    function validArtifact($id) {
1327
        $sql = "SELECT * FROM artifact a, artifact_group_list agl WHERE ".
1328
            "a.group_artifact_id = agl.group_artifact_id AND a.artifact_id=". db_ei($id) ." AND ".
1329
            "agl.status = 'A'";
1330
        $res = db_query($sql);
1331
        if ( db_numrows($res) >= 1 )
1332
            return true;
1333
        else
1334
            return false;
1335
    }
1336
        
1337
1338
    /**
1339
     * Insert a artifact dependency with the current one
1340
     *
1341
     * @param id: the artifact id
1342
     *
1343
     * @return boolean
1344
     */
1345
    function insertDependency($id) {
1346
        $sql = "INSERT INTO artifact_dependencies (artifact_id,is_dependent_on_artifact_id) ".
1347
            "VALUES (". db_ei($this->getID()) .",". db_ei($id) .")";
1348
        //echo $sql;
1349
        $res = db_query($sql);
1350
        return ($res);
1351
        
1352
    }
1353
1354
1355
    /**
1356
     * Delete all the CC Names of this Artifact
1357
     */
1358
    function deleteAllCC() {
1359
	$sql = "SELECT artifact_cc_id FROM artifact_cc WHERE artifact_id=". db_ei($this->getID()) ;
1360
	$res = db_query($sql);
1361
	if (db_numrows($res) > 0) {
1362
		for ($i=0;$i<db_numrows($res);$i++) {
1363
			if ($i==0) $ccNames = db_result($res,$i,'artifact_cc_id');
1364
			else $ccNames .= ",".db_result($res,$i,'artifact_cc_id');
1365
		}
1366
		$sql = "DELETE FROM artifact_cc WHERE artifact_cc_id IN (". db_es($ccNames) .") AND artifact_id=". db_ei($this->getID()) ;
1367
		$res_del = db_query($sql);
1368
		if (!$res_del) return false; 
1369
	}
1370
	return true;
1371
    }
1372
1373
     /**
1374
      * Delete all the dependencies of this Artifact
1375
      */
1376
     function deleteAllDependencies() {
1377
	$sql = "SELECT is_dependent_on_artifact_id FROM artifact_dependencies WHERE artifact_id=". db_ei($this->getID()) ;
1378
	$res = db_query($sql);
1379
	if (db_numrows($res) > 0) {
1380
		for ($i=0;$i<db_numrows($res);$i++) {
1381
			if ($i==0) $dependencies = db_result($res,$i,'is_dependent_on_artifact_id');
1382
			else $dependencies .= ",".db_result($res,$i,'is_dependent_on_artifact_id');
1383
		}
1384
		$sql = "DELETE FROM artifact_dependencies WHERE is_dependent_on_artifact_id IN (". db_es($dependencies) .") AND artifact_id=". db_ei($this->getID()) ;
1385
		$res_del = db_query($sql);
1386
		if (!$res_del) return false; 
1387
	}
1388
	return true;
1389
     }
1390
1391
1392
    /**
1393
     * Insert artifact dependencies
1394
     *
1395
     * @param artifact_id_dependent: list of artifact which are depend on (comma sperator)
1396
     * @param changes (OUT): list of changes
1397
     *
1398
     * @return boolean
1399
     */
1400
    function addDependencies($artifact_id_dependent,&$changes,$masschange, $import = false) {
1401
        if ( !$artifact_id_dependent ) 
1402
            return true;
1403
        
1404
        $ok = true;
1405
        $ids = explode(",",$artifact_id_dependent);
1406
        while (list(,$id) = each($ids)) {
1407
            // Add this id only if not already exist
1408
            //echo "add id=".$id."<br>";
1409
            
1410
            // Remove potential spaces (if the list of IDs are entered like that : 171, 765, 555)
1411
            $id = trim($id);
1412
            
1413
            // Check existance
1414
            if (!$this->validArtifact($id)) {
1415
                
1416
                // at import stage, $id can have value "None" or "Aucun"
1417
                if ( ! $import || $id != $GLOBALS['Language']->getText('global','none')) {
1418
                    $ok = false;
1419
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('tracker_common_artifact','invalid_art',$id));
1420
                }
1421
            }            
1422
            if ($ok && ($id != $this->getID()) && !$this->existDependency($id)) {
1423
                $res = $this->insertDependency($id);
1424
                if (!$res) { $ok = false; }
1425
            }
1426
        }
1427
        
1428
        if (!$ok) {
1429
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('tracker_common_artifact','depend_add_fail',$this->getID()));
1430
        } else {
1431
            $changes['Dependencies']['add'] = $artifact_id_dependent;
1432
        }
1433
        return $ok;
1434
    }
1435
1436
    /**
1437
     * Delete an artifact id from the dependencies list
1438
     *
1439
     * @param dependent_on_artifact_id: artifact id which is depend on
1440
     * @param changes (OUT): list of changes
1441
     *
1442
     * @return boolean
1443
     */
1444
    function deleteDependency($dependent_on_artifact_id,&$changes) {
1445
        global $Language;
1446
        
1447
        // Delete the dependency
1448
        $sql = "DELETE FROM artifact_dependencies WHERE is_dependent_on_artifact_id=". db_ei($dependent_on_artifact_id) ." AND artifact_id=". db_ei($this->getID()) ;
1449
        $res2 = db_query($sql);
1450
        if (!$res2) {
1451
            $GLOBALS['Response']->addFeedback('error', " - Error deleting dependency $dependent_on_artifact_id: ".db_error($res2));
1452
            return false;
1453
        } else {
1454
            $GLOBALS['Response']->addFeedback('info', $Language->getText('tracker_common_artifact','depend_removed'));
1455
            $changes['Dependencies']['del'] = $dependent_on_artifact_id;
1456
            return true;
1457
        }
1458
    }
1459
1460
    /**
1461
     * Delete a followup comment from the artifact
1462
     *
1463
     * @param aid: the artifact id
1464
     * @param comment_id: the followup comment id
1465
     *
1466
     * @return boolean
1467
     */    
1468
    function deleteFollowupComment($aid,$comment_id) {
1469
        if ($this->userCanEditFollowupComment($comment_id)) {
1470
            //Delete the followup comment
1471
            $sel = 'SELECT field_name, new_value FROM artifact_history'
1472
                .' WHERE (field_name = "comment" OR field_name LIKE "lbl_%_comment")'
1473
                .' AND artifact_history_id = '. db_ei($comment_id) 
1474
                .' AND artifact_id = '. db_ei($aid) ;
1475
            $res = db_query($sel);
1476
            $new_value = db_result($res,0,'new_value');
1477
            if ($res) {
1478
                $fname = db_result($res,0,'field_name');
1479
                if (preg_match("/^(lbl_)/",$fname) && preg_match("/(_comment)$/",$fname)) {
1480
                    $comment_lbl = $fname;
1481
                } else {
1482
                    $comment_lbl = "lbl_".$comment_id."_comment";    
1483
                }    		
1484
                //now add a new history entry
1485
                $this->addHistory($comment_lbl,$new_value,'',false,false,$comment_id);
1486
                $GLOBALS['Response']->addFeedback('info',$GLOBALS['Language']->getText('tracker_common_artifact','comment_removed'));
1487
                return true;
1488
            } else {
1489
                $GLOBALS['Response']->addFeedback('error',$GLOBALS['Language']->getText('tracker_common_artifact','err_del_comment',array($comment_id,db_error($res))));
1490
                return false;
1491
            }
1492
        } else {
1493
            $this->setError($GLOBALS['Language']->getText('tracker_common_artifact','err_del_comment', array($comment_id, $GLOBALS['Language']->getText('include_exit','perm_denied'))));
1494
            $GLOBALS['Response']->addFeedback('error',$GLOBALS['Language']->getText('tracker_common_artifact','err_del_comment', array($comment_id, $GLOBALS['Language']->getText('include_exit','perm_denied'))));
1495
            return false;
1496
        }
1497
1498
    }
1499
    
1500
    /**
1501
     * Return if the status is closed status
1502
     *
1503
     * @param status: the status
1504
     *
1505
     * @return boolean
1506
     */
1507
    function isStatusClosed($status) {
1508
        return (($status == '3') || ($status == '10') );
1509
    }
1510
1511
1512
    /**
1513
     * get all the field values for this artifact
1514
     *
1515
     * @return array
1516
     */
1517
    function getFieldsValues() {
1518
1519
        // get the artifact data
1520
        $this->fetchData($this->getID());
1521
        return $this->data_array;
1522
    }
1523
1524
    /**
1525
     * Return the user (user_id) that have posted follow up (comment_id) 
1526
     *
1527
     * @return int
1528
     */
1529
    function getCommenter($comment_id) {
1530
      
1531
        $sql = 'SELECT mod_by FROM artifact_history'
1532
			.' WHERE artifact_id='. db_ei($this->getID())
1533
			.' AND field_name="comment"'
1534
			.' AND mod_by != 100'
1535
			.' AND artifact_history_id='. db_ei($comment_id);
1536
	$res = db_query($sql);
1537
	return db_result($res,0,'mod_by');
1538
      
1539
    }
1540
    
1541
    /**
1542
     * Return the users that have posted follow ups 
1543
     *
1544
     * @return array
1545
     */
1546
    function getCommenters() {
1547
        $sql="SELECT DISTINCT mod_by FROM artifact_history ".
1548
	  "WHERE artifact_id=". db_ei($this->getID()) ." ".
1549
            "AND field_name = 'comment' AND mod_by != 100";
1550
        return db_query($sql);
1551
    }
1552
1553
    /**
1554
     * Return the mails of anonymous users that have posted follow ups 
1555
     *
1556
     * @return array
1557
     */
1558
    function getAnonymousCommenters() {
1559
        $sql="SELECT DISTINCT email FROM artifact_history ".
1560
	  "WHERE artifact_id=". db_ei($this->getID()) ." ".
1561
            "AND field_name = 'comment' ".
1562
	  "AND mod_by = 100";
1563
        return db_query($sql);
1564
    }
1565
1566
    /**
1567
     * Get follow-up comment text
1568
     * 
1569
     * @param Integer $comment_id
1570
     * 
1571
     * @return String
1572
     */
1573
    function getFollowup($comment_id) {
1574
        $res = $this->getFollowUpDetails($comment_id);
1575
        return $res['new_value'];
1576
    }
1577
1578
    /**
1579
     * Get all details of a follow-up comment
1580
     * 
1581
     * @param Integer $comment_id
1582
     * 
1583
     * @return Array
1584
     */
1585
    function getFollowUpDetails($comment_id) {
1586
        $sql = 'SELECT * FROM artifact_history'
1587
        .' WHERE (field_name="comment" OR field_name LIKE "lbl_%_comment")'
1588
        .' AND artifact_history_id='. db_ei($comment_id)
1589
        .' AND new_value <> ""';
1590
        $res = db_query($sql);
1591
        if ($res && !db_error($res) && db_numrows($res) == 1) {
1592
            return db_fetch_array($res);
1593
        }
1594
        return false;
1595
    }
1596
1597
    /**
1598
     * Return the follow ups 
1599
     *
1600
     * @return array
1601
     */
1602
    function getFollowups () {
1603
        global $art_field_fact;
1604
1605
    	$flup_array = array();
1606
    	$qry = 'SELECT artifact_history_id, date FROM artifact_history'.
1607
    			' WHERE artifact_id = '. db_ei($this->getID()) .
1608
    			' AND field_name = "comment"';
1609
    	$res = db_query($qry);
1610
    	while ($row = db_fetch_array($res)) {
1611
    		$ahid = $row['artifact_history_id'];
1612
    		$fname = "lbl_".$ahid."_comment";
1613
    		$sel = 'SELECT NULL FROM artifact_history'.
1614
    						' WHERE field_name = "'. db_es($fname) .'"'. 
1615
    						' AND artifact_id = '. db_ei($this->getID()) ;
1616
    		$result = db_query($sel);
1617
    		if (db_numrows($result) < 1) {
1618
    			//the followup comment was not edited/removed ==> add it to the list of comments to be displayed    			
1619
    			$flup_array[$ahid] = $row['date'];
1620
    		} else {
1621
    			//pick the latest
1622
    			$latest = 'SELECT artifact_history_id , new_value FROM artifact_history'.
1623
    								' WHERE field_name = "'. db_es($fname) .'"'. 
1624
    								' AND artifact_id = '. db_ei($this->getID()) .
1625
    								' AND date = (SELECT MAX(date) FROM artifact_history'.
1626
    								'             WHERE field_name = "'. db_es($fname) .'"'. 
1627
    								'             AND artifact_id = '. db_ei($this->getID()) .')';
1628
    			$res_latest = db_query($latest);
1629
    			$new_value = db_result($res_latest,0,'new_value');
1630
    			if ($new_value <> '') {
1631
    				//if new_value eq '' ==> the followup comment was removed, don't display it
1632
    				$art_hist_id = db_result($res_latest,0,'artifact_history_id');    				
1633
    				$flup_array[$art_hist_id] = $row['date'];
1634
    			}
1635
    		}
1636
    	}
1637
    	arsort($flup_array);
1638
    	$comment_array = array_keys($flup_array);    	
1639
    	
1640
    	$field = $art_field_fact->getFieldFromName('comment_type_id');
1641
    	if ( $field ) {
1642
    		// Look for project specific values first
1643
    		$sql="SELECT DISTINCT artifact_history.artifact_history_id, artifact_history.format, artifact_history.artifact_id,artifact_history.field_name,artifact_history.old_value,artifact_history.new_value,artifact_history.date,user.user_name,artifact_history.mod_by,artifact_history.email,artifact_history.type AS comment_type_id,artifact_field_value_list.value AS comment_type ".
1644
    		"FROM artifact_history,artifact_field_value_list,artifact_field,user ".
1645
    		"WHERE artifact_history.artifact_id=". db_ei($this->getID()) ." ".
1646
    		"AND (artifact_history.field_name = 'comment' OR artifact_history.field_name LIKE 'lbl_%_comment') ".
1647
    		"AND artifact_history.mod_by=user.user_id ".
1648
    		"AND artifact_history.type = artifact_field_value_list.value_id ".
1649
    		"AND artifact_history.artifact_history_id IN (". db_es(implode(',',$comment_array)) .") ".
1650
    		"AND artifact_field_value_list.field_id = artifact_field.field_id ".
1651
    		"AND artifact_field_value_list.group_artifact_id = artifact_field.group_artifact_id ".
1652
    		"AND artifact_field.group_artifact_id =". db_ei($this->ArtifactType->getID()) ." ".
1653
    		"AND artifact_field.field_name = 'comment_type_id' ".
1654
    		"ORDER BY FIELD(artifact_history_id, ". db_es(implode(',',$comment_array)) .")";    		    	
1655
    		$res_value = db_query($sql);
1656
    		$rows=db_numrows($res_value);
1657
1658
    		//echo "sql=".$sql." - rows=".$rows."<br>";
1659
    	} else {
1660
    		// Look for project specific values first
1661
    		$sql="SELECT DISTINCT artifact_history.artifact_history_id, artifact_history.format, artifact_history.artifact_id,artifact_history.field_name,artifact_history.old_value,artifact_history.new_value,artifact_history.date,user.user_name,artifact_history.mod_by,artifact_history.email,artifact_history.type AS comment_type_id,null AS comment_type ".
1662
    		"FROM artifact_history,user ".
1663
    		"WHERE artifact_history.artifact_id=".$this->getID()." ".
1664
    		"AND (artifact_history.field_name = 'comment' OR artifact_history.field_name LIKE 'lbl_%_comment') ".
1665
    		"AND artifact_history.mod_by=user.user_id ".
1666
    		"AND artifact_history.artifact_history_id IN (". db_es(implode(',',$comment_array)) .") ".
1667
    		"ORDER BY FIELD(artifact_history_id, ". db_es(implode(',',$comment_array)) .")";    		
1668
    		$res_value = db_query($sql);
1669
    		$rows=db_numrows($res_value);
1670
1671
    	}
1672
    	return($res_value);
1673
    	
1674
    }
1675
        
1676
    /**
1677
     * Return the history events for this artifact (excluded comment events - See followups)
1678
     *
1679
     * @return array
1680
     */
1681
    function getHistory () {
1682
1683
    	//Addition of new followup comments is not recorded in history (update and removal of followups is recorded)
1684
		$sql="SELECT artifact_history.field_name,artifact_history.old_value,artifact_history.new_value,artifact_history.date,artifact_history.type,user.user_name ".
1685
            "FROM artifact_history,user ".
1686
            "WHERE artifact_history.mod_by=user.user_id ".            
1687
            "AND artifact_id=". db_ei($this->getID()) .
1688
            " AND artifact_history.field_name <> 'comment' ".
1689
	    	"ORDER BY artifact_history.date DESC";
1690
    	return db_query($sql);
1691
    }
1692
1693
    /**
1694
     * Return the CC list values
1695
     *
1696
     * @return array
1697
     */
1698
    function getCCList() {
1699
                
1700
        $sql="SELECT artifact_cc_id,artifact_cc.email,artifact_cc.added_by,artifact_cc.comment,artifact_cc.date,user.user_name ".
1701
            "FROM artifact_cc,user ".
1702
            "WHERE added_by=user.user_id ".
1703
            "AND artifact_id=". db_ei($this->getID()) ." ORDER BY date DESC";
1704
        return db_query($sql);
1705
    }
1706
1707
    /**
1708
     * Return the user ids of registered users in the CC list
1709
     *
1710
     * @return array
1711
     */
1712
    function getCCIdList() {
1713
                
1714
        $sql="SELECT u.user_id ".
1715
	  "FROM artifact_cc cc, user u ".
1716
	  "WHERE cc.email = u.user_name ".
1717
	  "AND cc.artifact_id=". db_ei($this->getID()) ;
1718
	$res = db_query($sql);
1719
	
1720
        return util_result_column_to_array($res);
1721
    }
1722
1723
    /**
1724
     * Return the CC list emails only
1725
     *
1726
     * @return string
1727
     */
1728
    function getCCEmails() {
1729
                
1730
        $sql="SELECT email ".
1731
            "FROM artifact_cc ".
1732
            "WHERE artifact_id=". db_ei($this->getID()) ." ORDER BY date DESC";
1733
        $result = db_query($sql);
1734
	$rows=db_numrows($result);
1735
        if ($rows <= 0) {
1736
	    return '';
1737
	} else {
1738
	    $email_arr=array();
1739
	    for ($i=0; $i < $rows; $i++) {
1740
	        $email_arr[] = db_result($result, $i, 'email');
1741
	    }
1742
	    $old_value = join(",",$email_arr);
1743
	    return $old_value;
1744
	}
1745
    }
1746
1747
    /**
1748
     * Return a CC list values
1749
     *
1750
     * @param artifact_cc_id: the artifact cc id
1751
     *
1752
     * @return array
1753
     */
1754
    function getCC($artifact_cc_id) {
1755
                
1756
        $sql="SELECT artifact_cc_id,artifact_cc.email,artifact_cc.added_by,artifact_cc.comment,artifact_cc.date,user.user_name ".
1757
            "FROM artifact_cc,user ".
1758
            "WHERE artifact_cc_id=". db_ei($artifact_cc_id) ." ".
1759
            "AND added_by=user.user_id";
1760
        $res = db_query($sql);
1761
        return db_fetch_array($res);
1762
    }
1763
1764
    /**
1765
     * Return the artifact dependencies values
1766
     *
1767
     * @return array
1768
     */
1769
    function getDependencies() {
1770
                
1771
    	$sql="SELECT d.artifact_depend_id, d.is_dependent_on_artifact_id, d.artifact_id, a.summary, afvl.value as status, ag.group_artifact_id, ag.name, g.group_id, g.group_name ".
1772
            "FROM artifact_dependencies d, artifact_group_list ag, groups g, artifact a, artifact_field_value_list afvl, artifact_field f ".
1773
            "WHERE d.is_dependent_on_artifact_id = a.artifact_id AND ".
1774
            "afvl.field_id = f.field_id AND ".
1775
            "f.group_artifact_id = a.group_artifact_id AND ".
1776
            "f.field_name = 'status_id' AND ".
1777
            "afvl.value_id = a.status_id AND ".
1778
            "afvl.group_artifact_id = a.group_artifact_id AND ".
1779
            "a.group_artifact_id = ag.group_artifact_id AND ".
1780
            "d.artifact_id = ". db_ei($this->getID()) ." AND ".
1781
            "ag.group_id = g.group_id ORDER BY a.artifact_id";
1782
        //echo "sql=$sql<br>";
1783
        return db_query($sql);
1784
    }
1785
1786
    /**
1787
     * Return the artifact inverse dependencies values
1788
     *
1789
     * @return array
1790
     */
1791
    function getInverseDependencies() {
1792
                
1793
        $sql="SELECT d.artifact_depend_id, d.is_dependent_on_artifact_id, d.artifact_id, a.summary, afvl.value as status, ag.group_artifact_id, ag.name, g.group_id, g.group_name ".
1794
            "FROM artifact_dependencies d, artifact_group_list ag, groups g, artifact a, artifact_field_value_list afvl, artifact_field f ".
1795
            "WHERE d.artifact_id = a.artifact_id AND ".
1796
            "afvl.field_id = f.field_id AND ".
1797
            "f.group_artifact_id = a.group_artifact_id AND ".
1798
            "f.field_name = 'status_id' AND ".
1799
            "afvl.value_id = a.status_id AND ".
1800
            "afvl.group_artifact_id = a.group_artifact_id AND ".
1801
            "a.group_artifact_id = ag.group_artifact_id AND ".
1802
            "d.is_dependent_on_artifact_id = ". db_ei($this->getID()) ." AND ".
1803
            "ag.group_id = g.group_id ORDER BY a.artifact_id";
1804
        //echo "sql=$sql<br>";
1805
        return db_query($sql);
1806
    }
1807
1808
    /**
1809
     * Return the names of attached files
1810
     *
1811
     * @return string
1812
     */
1813
    function getAttachedFileNames () {
1814
        $sql="SELECT filename ".
1815
            "FROM artifact_file ".
1816
            "WHERE artifact_id=". db_ei($this->getID()) ." ORDER BY adddate DESC";
1817
        $result = db_query($sql);
1818
	$rows=db_numrows($result);
1819
        if ($rows <= 0) {
1820
	    return '';
1821
	} else {
1822
	    $name_arr=array();
1823
	    for ($i=0; $i < $rows; $i++) {
1824
	        $name_arr[] = db_result($result, $i, 'filename');
1825
	    }
1826
	    $old_value = join(',',$name_arr);
1827
	    return $old_value;
1828
	}
1829
    }
1830
1831
    /**
1832
     * Return the attached files
1833
     *
1834
     * @return array
1835
     */
1836
    function getAttachedFiles () {
1837
        $sql="SELECT id,artifact_id,filename,filesize,filetype,description,bin_data,adddate,user.user_name ".
1838
            "FROM artifact_file,user ".
1839
            "WHERE submitted_by=user.user_id ".
1840
            "AND artifact_id=". db_ei($this->getID()) ." ORDER BY adddate DESC";
1841
        //echo "sql=$sql<br>";
1842
        return db_query($sql);
1843
    }
1844
1845
    /**
1846
     * Return a attached file
1847
     *
1848
     * @param id: the file id
1849
     *
1850
     * @return array
1851
     */
1852
    function getAttachedFile ($id) {
1853
        $sql="SELECT id,filename,filesize,description,adddate,user.user_name ".
1854
            "FROM artifact_file,user ".
1855
            "WHERE submitted_by=user.user_id ".
1856
            "AND id=". db_ei($id) ;
1857
        //echo "sql=$sql<br>";
1858
        $res = db_query($sql);
1859
        return db_fetch_array($res);
1860
    }
1861
1862
    function checkAssignees ($field_name,$result,$art_field_fact,$changes,&$user_ids) {
1863
1864
        // check assignee  notification preferences
1865
        // Never notify user 'none' (id #100)
1866
        // Check for field 'assigned_to' (SelectBox)
1867
	// assigned to can also be a multi_select_box
1868
	$field = $art_field_fact->getFieldFromName($field_name);
1869
        if ( $field ) {
1870
		if ($field->getDisplayType() == "MB") {
1871
            		$field_value = $field->getValues($this->getID());
1872
			if ($field_value && (count($field_value) > 0) ) {
1873
				$val_func = $field->getValueFunction();
1874
				if ( $val_func[0] != "") {
1875
                			while (list (,$user_id)=each ($field_value)) {
1876
                    				if ( ($user_id) && ($user_id != 100) ) {
1877
                    				    $curr_assignee = UserManager::instance()->getUserById($user_id);	
1878
						            if ((!array_key_exists($user_id, $user_ids) || !$user_ids[$user_id]) && 
1879
						                 $this->ArtifactType->checkNotification($user_id, 'ASSIGNEE', $changes) && 
1880
						                 $this->userCanView($user_id) && 
1881
                                         $curr_assignee->isActive() || $curr_assignee->isRestricted()
1882
                                         ) {
1883
						      //echo "DBG - ASSIGNEE - user=$user_id<br>";
1884
						      $user_ids[$user_id] = true;
1885
                        				}
1886
						}
1887
                    			}
1888
                		} else {
1889
					// we handle now also the case that the assigned_to field is NOT BOUND to a predefined value list
1890
					// we accept only names that correspond to codendi user names
1891
					while (list (,$value_id)=each ($field_value)) {
1892
						$user_name = $field->getValue($this->ArtifactType->getID(),$value_id);
1893
						$res_u = user_get_result_set_from_unix($user_name);
1894
						$user_id = db_result($res_u,0,'user_id');
1895
                    				if ( ($user_id) && ($user_id != 100) ) {
1896
                    				    $curr_assignee = UserManager::instance()->getUserById($user_id);	
1897
                        				if (!$user_ids[$user_id] && 
1898
							    $this->ArtifactType->checkNotification($user_id, 'ASSIGNEE', $changes) &&
1899
							    $this->userCanView($user_id) &&
1900
                                $curr_assignee->isActive() || $curr_assignee->isRestricted()
1901
                                ) {
1902
							    //echo "DBG - ASSIGNEE - user=$user_id<br>";
1903
							    $user_ids[$user_id] = true;
1904
                        				}
1905
                    				}
1906
					}	
1907
				}
1908
            		}
1909
		} else {
1910
			// display type is SB
1911
        	$user_id = isset($result[$field_name])?$result[$field_name]:null;
1912
			$val_func = $field->getValueFunction();
1913
			if ($val_func[0] == "") {
1914
				// we handle now also the case that the assigned_to field is NOT BOUND to a predefined value list
1915
				// we accept only names that correspond to codendi user names
1916
				// so: this user_id is not a user_id but a value_id
1917
				$user_name = $field->getValue($this->ArtifactType->getID(),$user_id);
1918
				$res = user_get_result_set_from_unix($user_name);
1919
				$user_id = db_result($res,0,'user_id');
1920
			}
1921
        		if ( ($user_id) && ($user_id != 100) ) {
1922
        		        $curr_assignee = UserManager::instance()->getUserById($user_id);
1923
            			if ((!array_key_exists($user_id, $user_ids) || !$user_ids[$user_id]) && 
1924
				    $this->ArtifactType->checkNotification($user_id, 'ASSIGNEE', $changes) &&
1925
				    $this->userCanView($user_id) && 
1926
                    $curr_assignee->isActive() || $curr_assignee->isRestricted()) {
1927
				    //echo "DBG - ASSIGNEE - user=$user_id<br>";
1928
				    $user_ids[$user_id] = true;
1929
            			}
1930
        		}
1931
1932
		}
1933
	}
1934
        
1935
1936
        // check old assignee  notification preferences if assignee was just changed
1937
        // Never notify user 'none' (id #100)
1938
        if (isset($changes[$field_name]) && isset($changes[$field_name]['del'])) {
1939
            $user_name = $changes[$field_name]['del'];
1940
        } else {
1941
            unset($user_name);
1942
        }
1943
        if (isset($user_name) && $user_name) {
1944
		//echo " verify deleted assigned_to - user_name=$user_name ";
1945
	    $del_arr = explode(",",$user_name);
1946
	    while (list (,$uname)=each ($del_arr)) {
1947
		//echo " uname=$uname ";
1948
            	$res_oa = user_get_result_set_from_unix($uname);
1949
            	$user_id = db_result($res_oa,0,'user_id');
1950
            $curr_assignee = UserManager::instance()->getUserById($user_id);
1951
            	if ($user_id != 100 && 
1952
		    !isset($user_ids[$user_id]) && 
1953
		    $this->ArtifactType->checkNotification($user_id, 'ASSIGNEE', $changes) &&
1954
		    $this->userCanView($user_id) &&
1955
            $curr_assignee && (
1956
            $curr_assignee->isActive() || $curr_assignee->isRestricted())) {
1957
                	//echo "DBG - ASSIGNEE OLD - user=$user_id<br>";
1958
                	$user_ids[$user_id] = true;
1959
            	}
1960
            }
1961
	}
1962
    }
1963
1964
1965
    
1966
    /**
1967
     *	  userCanView - determine if the user can view this artifact.
1968
     *
1969
     *	  @param $my_user_id	if not specified, use the current user id..
1970
     *	  @return boolean	user_can_view.
1971
     */
1972
    function userCanView($my_user_id=0) {
1973
1974
        if (!$my_user_id) {
1975
            $u = UserManager::instance()->getCurrentUser();
1976
            $my_user_id = $u->getId();
1977
        } else {
1978
            $u = UserManager::instance()->getUserById($my_user_id);
1979
        }
1980
        // Super-user and Tracker admin have all rights to see even artfact that are restricted to all users
1981
        if ($u->isSuperUser() || $u->isTrackerAdmin($this->ArtifactType->getGroupID(),$this->ArtifactType->getID())) {
1982
            return true;
1983
        }
1984
      
1985
1986
        //Individual artifact permission
1987
        $can_access = ! $this->useArtifactPermissions();
1988
        if (!$can_access) {
1989
            $res=permission_db_authorized_ugroups('TRACKER_ARTIFACT_ACCESS',$this->getID());
1990
            if (db_numrows($res) > 0) {
1991
                while ($row = db_fetch_array($res)) {
1992
                    if (ugroup_user_is_member($my_user_id, $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
1993
                        $can_access = true;
1994
                    }
1995
                }
1996
            }
1997
        }
1998
        if ($can_access) {
1999
            // Full access
2000
            $res=permission_db_authorized_ugroups('TRACKER_ACCESS_FULL',$this->ArtifactType->getID());
2001
            if (db_numrows($res) > 0) {
2002
                while ($row = db_fetch_array($res)) {
2003
                    if (ugroup_user_is_member($my_user_id, $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
2004
                        return true;
2005
                    }
2006
                }
2007
            }
2008
    
2009
            // 'submitter' access
2010
            $res=permission_db_authorized_ugroups('TRACKER_ACCESS_SUBMITTER',$this->ArtifactType->getID());
2011
            if (db_numrows($res) > 0) {
2012
                while ($row = db_fetch_array($res)) {
2013
                    if (ugroup_user_is_member($my_user_id, $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
2014
                        // check that submitter is also a member
2015
                        if (ugroup_user_is_member($this->getSubmittedBy(), $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
2016
                            return true;
2017
                        }
2018
                    }
2019
                }
2020
            }
2021
            // 'assignee' access
2022
            $res=permission_db_authorized_ugroups('TRACKER_ACCESS_ASSIGNEE',$this->ArtifactType->getID());
2023
            if (db_numrows($res) > 0) {
2024
                while ($row = db_fetch_array($res)) {
2025
                    if (ugroup_user_is_member($my_user_id, $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
2026
                        // check that one of the assignees is also a member
2027
                        if (ugroup_user_is_member($this->getValue('assigned_to'), $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
2028
                            return true;
2029
                        }
2030
    
2031
                        // multi-assigned to
2032
                        $multi_assigned=$this->getMultiAssignedTo();
2033
                        if (is_array($multi_assigned)) {
2034
                            foreach ($multi_assigned as $assigned) {
2035
                                if (ugroup_user_is_member($assigned, $row['ugroup_id'], $this->ArtifactType->Group->getID(), $this->ArtifactType->getID())) {
2036
                                    return true;
2037
                                }
2038
                            }
2039
                        }
2040
                    }
2041
                }
2042
            }
2043
        }
2044
        return false;
2045
    } 
2046
    
2047
    /**
2048
     *	getExtraFieldData - get an array of data for the extra fields associated with this artifact
2049
     *
2050
     *      the array returned looks like 
2051
     *          array(
2052
     *             [field_id] => fieldvalues
2053
     *             [field_id] => fieldvalues
2054
     *          )
2055
     *      for multi select boxes, the values are separated by a comma
2056
     *
2057
     *	@return	array	array of data
2058
     */
2059
    function &getExtraFieldData() {
2060
    	global $art_field_fact;
2061
    	$extrafielddata = array();
2062
2063
    	// now get the values for generic fields if any
2064
        $sql = "SELECT * FROM artifact_field_value WHERE artifact_id='". db_ei($this->getID()) ."'";
2065
        $res=db_query($sql);
2066
        if (!$res || db_numrows($res) < 1) {
2067
            // if no result then it is possible that there isn't any generic fields
2068
            return;
2069
        }
2070
        // Walk the database result (possible to get several values with a same field_id (multi select box))
2071
        while ($row = db_fetch_array($res)) {
2072
            $data_fields[$row['field_id']][] = $row;
2073
        }
2074
        
2075
        // compute the extrafielddata array by walking the data_fields array
2076
        $extrafielddata = array();
2077
        foreach ($data_fields as $field_id => $data_field) {
2078
            $current_field = $art_field_fact->getFieldFromId($field_id);
2079
            if (isset($current_field) && $current_field && !$current_field->isError()) {
2080
                // $values contains the values of the field
2081
                $values = array();
2082
                foreach ($data_field as $data_value) {
2083
                    $values[] = $data_value[$current_field->getValueFieldName()];
2084
                }
2085
                // if there is more than one value, we separate them with a comma.
2086
                // if not, implode will return the single value.
2087
                $extrafielddata[$field_id] = implode(",", $values);
2088
            }
2089
        }
2090
        return $extrafielddata;
2091
    }
2092
2093
2094
    /**
2095
     * Build an array of user_ids using the changes array
2096
     *
2097
     * @param changes (IN): array of changes
2098
     * 
2099
     * @param concerned_ids (OUT): user_ids of concerned users (attention user_ids are stored as keys)
2100
     * @param concerned_addresses (OUT): email addresses of anonymous users (for instance in CC addresses)
2101
     *
2102
     */
2103
    function buildNotificationArrays($changes,&$concerned_ids,&$concerned_addresses) {
2104
                
2105
        global $art_field_fact,$Language;
2106
        
2107
        // Rk: we store user ids in a hash to make sure they are only
2108
        // stored once. Normally if an email is repeated several times sendmail
2109
        // would take care of it but I prefer taking care of it now.
2110
	// We also use the user_ids hash to check if a user has already been selected for 
2111
        // notification. If so it is not necessary to check it again in another role.
2112
        $concerned_ids = array();
2113
	$concerned_addresses = array();
2114
	$concerned_watchers = array();
2115
2116
        
2117
        
2118
        // check submitter notification preferences
2119
        $user_id = $this->getSubmittedBy();
2120
        $submitter = UserManager::instance()->getUserById($user_id);
2121
        if ($user_id != 100 && ($submitter->isActive() || $submitter->isRestricted())) {
2122
	  if ($this->ArtifactType->checkNotification($user_id, 'SUBMITTER', $changes) && $this->userCanView($user_id)) {
2123
	        //echo "DBG - SUBMITTER - user=$user_id<br>";
2124
                $concerned_ids[$user_id] = true;
2125
            }
2126
        }
2127
        
2128
	// Retrieve field values for the assigned_to, multi_assigned_to value
2129
        $result = $this->getFieldsValues();
2130
	$this->checkAssignees("assigned_to",$result,$art_field_fact,$changes,$concerned_ids);
2131
	$this->checkAssignees("multi_assigned_to",$result,$art_field_fact,$changes,$concerned_ids);
2132
	
2133
2134
	// check all CC 
2135
        // (a) check all the people in the current CC list
2136
        // (b) check the CC that has just been removed if any and see if she
2137
        // wants to be notified as well
2138
        // if the CC indentifier is an email address then notify in any case
2139
        // because this user has no personal setting
2140
        $res_cc = $this->getCCList();
2141
        $arr_cc = array();
2142
        if ($res_cc && (db_numrows($res_cc) > 0)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res_cc 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...
2143
            while ($row = db_fetch_array($res_cc)) {
2144
                $arr_cc[] = $row['email'];
2145
            }
2146
        }
2147
        if (isset($changes['CC']) && isset($changes['CC']['del']) && $changes['CC']['del'] ) {
2148
            // Only one CC can be deleted at once so just append it to the list....
2149
            $arr_cc[] = $changes['CC']['del'];
2150
        }
2151
                        
2152
        while (list(,$cc) = each($arr_cc)) {
2153
            //echo "DBG - CC=$cc<br>";
2154
            if (validate_email($cc)) {
2155
	        //echo "DBG - CC email - email=".util_normalize_email($cc)."<br>";
2156
                $concerned_addresses[util_normalize_email($cc)] = true;
2157
            } else {
2158
                $res = user_get_result_set_from_unix($cc);
2159
                $user_id = db_result($res,0,'user_id');
2160
                if (!isset($concerned_ids[$user_id]) && $this->ArtifactType->checkNotification($user_id, 'CC', $changes)) {
2161
		    //echo "DBG - CC - user=$user_id<br>";
2162
                    $concerned_ids[$user_id] = true;
2163
                }
2164
            }
2165
        } // while
2166
        
2167
        
2168
        // check all commenters
2169
        $res_com = $this->getCommenters();
2170
        if (db_numrows($res_com) > 0) {
2171
            while ($row = db_fetch_array($res_com)) {
2172
                $user_id = $row['mod_by'];
2173
                if (!isset($concerned_ids[$user_id]) && $this->ArtifactType->checkNotification($user_id, 'COMMENTER', $changes)) {
2174
		    //echo "DBG - COMMENTERS - user=$user_id<br>";
2175
                    $concerned_ids[$user_id] = true;
2176
                }
2177
            }
2178
        }
2179
        // check all anonymous commenters
2180
        $res_com = $this->getAnonymousCommenters();
2181
        if (db_numrows($res_com) > 0) {
2182
            while ($row = db_fetch_array($res_com)) {
2183
                $user_mail = $row['email'];
2184
		//echo "DBG - anon COMMENTERS - user=$user_mail<br>";
2185
		$concerned_addresses[$user_mail] = true;
2186
            }
2187
        }
2188
2189
	//check all watchers
2190
	foreach (array_keys($concerned_ids) as $watchee) {
2191
	  $db_res = $this->ArtifactType->getWatchers($watchee);
2192
	  while ($row_watcher = db_fetch_array($db_res)) {
2193
	    $watcher = $row_watcher['user_id'];
2194
        $concerned_watchers[$watcher] = true;
2195
	  }
2196
	}
2197
2198
	foreach (array_keys($concerned_watchers) as $watcher) {
2199
	  if (!$concerned_ids[$watcher]) $concerned_ids[$watcher] = true;
2200
	}
2201
    }
2202
2203
2204
2205
    /** group users to be notified of artifact changes
2206
     * groups are done with respect to ugroups and 
2207
     * their permissions on the artifact
2208
     * @param user_id an array of user ids
2209
     * return $user_sets array of arrays of user ids: 
2210
     * return $ugroup_sets array of arrays of ugroup_ids.
2211
     * the $user_sets keys correspond to the $ugroup_sets keys i.e.
2212
     * $ugroup_sets[x] are the ugroups that the users in $user_sets[x]
2213
     * belong to
2214
     */
2215
    function groupNotificationList($user_ids,&$user_sets,&$ugroup_sets) {
2216
      
2217
      $group_id = $this->ArtifactType->getGroupID();
2218
      $group_artifact_id = $this->ArtifactType->getID();
2219
2220
      $user_sets = array();
2221
      $ugroup_sets = array();
2222
      
2223
      //go through user_ids array:
2224
      //for each user have a look at which ugroups he belongs
2225
      
2226
2227
      foreach ($user_ids as $user_id) {
2228
	$specific_ugroups = ugroup_db_list_tracker_ugroups_for_user($group_id,$group_artifact_id,$user_id);
2229
	//echo "<br>specific_ugroups for $user_id = "; print_r($specific_ugroups);
2230
	$dynamic_ugroups = ugroup_db_list_dynamic_ugroups_for_user($group_id,$group_artifact_id,$user_id);
2231
	//echo "<br>dynamic_ugroups for $user_id = "; print_r($dynamic_ugroups);
2232
	$all_ugroups = array_merge($dynamic_ugroups, $specific_ugroups);
2233
	//echo "<br>all_ugroups for $user_id = "; print_r($all_ugroups);
2234
2235
	$found_gr = false;
2236
	while (list($x,$ug) = each($ugroup_sets)) {
2237
	  $diff1 = array_diff($ug,$all_ugroups);
2238
	  $diff2 = array_diff($all_ugroups,$ug);
2239
	  if ( empty($diff1) && empty($diff2) ) {
2240
	    // we found the magic users that are part of exactly the same ugroups as this user
2241
	    $gr = $user_sets[$x];
2242
	    $gr[] = $user_id;
2243
	    unset($user_sets[$x]);
2244
	    $user_sets[$x] = $gr;
2245
	    $found_gr = true;
2246
	    break;
2247
	  }
2248
	}
2249
	// if we didn't find users who have exactly the same permissions we have to add this user separately 
2250
	if (!$found_gr) {
2251
	  $user_sets[] = array($user_id);
2252
	  $ugroup_sets[] = $all_ugroups;
2253
	}
2254
      }
2255
      
2256
    }
2257
    
2258
    /**
2259
      * Checks if a user is allowed to delete and update a follow-up comment
2260
      *
2261
      * @param user_id 
2262
      * @param comment_id
2263
      *
2264
      * @return boolean
2265
      */    
2266
    function userCanEditFollowupComment($comment_id) {
2267
2268
    	//if user is not logged in, he cannot update/delete comments
2269
    	if (! user_isloggedin()) {
2270
    		return false;
2271
    	}
2272
    	
2273
    	//tracker admin can delete and update followup comments
2274
    	if ($this->ArtifactType->userIsAdmin(user_getid())) {    		
2275
    		return true;
2276
    	} 
2277
    	
2278
    	$com_res = $this->getOriginalCommentSubmitter($comment_id);
2279
    	$commenter = db_result($com_res,0,'mod_by'); 
2280
    	if ($commenter == user_getid()) {
2281
    		return true;
2282
    	} else {
2283
    		return false;
2284
    	}
2285
    }
2286
    
2287
    /**
2288
     * Checks if the comment was removed
2289
     * 
2290
     * @params int comment_id
2291
     * 
2292
     * @return boolean
2293
     */
2294
    function isFollowupCommentDeleted($comment_id) {
2295
    	
2296
    	$sql = 'SELECT artifact_id, new_value 
2297
                FROM artifact_history 
2298
                WHERE artifact_history_id = '. db_ei($comment_id) ;
2299
    	$res = db_query($sql);
2300
    	if (db_result($res,0,'new_value') == "") {
2301
    		return true;				
2302
    	}
2303
    	$lbl = "lbl_".$comment_id."_comment";
2304
    	$aid = db_result($res,0,'artifact_id');
2305
    	$qry = 'SELECT NULL FROM artifact_history'
2306
						.' WHERE artifact_id = '. db_ei($aid) 
2307
						.' AND field_name = "'. db_es($lbl) .'"'
2308
						.' AND new_value = ""';
2309
    	$result = db_query($qry);
2310
    	if (db_numrows($result) > 0) {
2311
    		return true;				
2312
    	} else {
2313
    		return false; 
2314
    	}
2315
    	
2316
    }
2317
    
2318
    /**
2319
     * Gets the original submitter of a follow-up comment
2320
     * 
2321
     * @param int comment_id
2322
     * 
2323
     * @return result set 
2324
     */
2325
    function getOriginalCommentSubmitter($comment_id) {
2326
    	
2327
    	$sql = 'SELECT field_name, mod_by, email 
2328
                FROM artifact_history
2329
                WHERE artifact_history_id = '. db_ei($comment_id) ;
2330
    	$res = db_query($sql);
2331
    	$field_name = db_result($res,0,'field_name');
2332
    	if ($field_name == "comment") {
2333
    		return $res;				
2334
    	} else if (preg_match("/^(lbl_)/",$field_name) && preg_match("/(_comment)$/",$field_name)) {
2335
    		// extract id of the original comment
2336
    		$id = (int) substr($field_name,4,-8);
2337
    		$qry = 'SELECT mod_by, email 
2338
                    FROM artifact_history
2339
                    WHERE artifact_history_id = '. db_ei($id) .'
2340
                    AND field_name = "comment"';
2341
    		$result = db_query($qry);
2342
    		return $result;				
2343
    	}
2344
    	
2345
    }
2346
2347
    /**
2348
     * Gets the original submission date of a follow-up comment
2349
     * 
2350
     * @param int comment_id
2351
     * 
2352
     * @return result set 
2353
     */
2354
    function getOriginalCommentDate($comment_id) {
2355
    	$sql = 'SELECT field_name, date
2356
                FROM artifact_history
2357
                WHERE artifact_history_id = '. db_ei($comment_id) ;
2358
    	$res = db_query($sql);
2359
    	$field_name = db_result($res,0,'field_name');
2360
    	if ($field_name == "comment") {
2361
    		return $res;				
2362
    	} else if (preg_match("/^(lbl_)/",$field_name) && preg_match("/(_comment)$/",$field_name)) {
2363
    		// extract id of the original comment 
2364
    		$id = (int) substr($field_name,4,-8);
2365
    		$qry = 'SELECT date
2366
                    FROM artifact_history
2367
                    WHERE artifact_history_id = '. db_ei($id) .'
2368
                    AND field_name = "comment"';
2369
    		$result = db_query($qry);
2370
    		return $result;				    		
2371
    	}    	
2372
    }    
2373
    
2374
        /**
2375
    * Send different messages to persons affected by this artifact with respect 
2376
	* to their different permissions 
2377
    *
2378
    * @param more_addresses: additional addresses
2379
    * @param changes: array of changes
2380
    *
2381
    * @return void
2382
    */
2383
    function mailFollowupWithPermissions($more_addresses=false,$changes=false) {
2384
      global $art_field_fact,$Language;
2385
        
2386
      // check if notification is temporarily stopped in this tracker
2387
      if (!$this->ArtifactType->getStopNotification()) {
2388
        $group = $this->ArtifactType->getGroup();
2389
        $group_artifact_id = $this->ArtifactType->getID();
2390
        $group_id = $group->getGroupId();
2391
        
2392
        // See who is going to receive the notification. Plus append any other email 
2393
        // given at the end of the list.
2394
        $withoutpermissions_concerned_addresses = array();
2395
        $this->buildNotificationArrays($changes, $concerned_ids, $concerned_addresses);
2396
        if ($more_addresses) {
2397
            foreach ($more_addresses as $address) {
0 ignored issues
show
Bug introduced by
The expression $more_addresses of type boolean is not traversable.
Loading history...
2398
                if ($address['address'] && $address['address'] != '') {
2399
                    $res_username = user_get_result_set_from_email($address['address'], false);
2400
                    if ($res_username && (db_numrows($res_username) == 1)) {
2401
                        $u_id = db_result($res_username,0,'user_id');
2402
                        if (!$address['check_permissions']) {
2403
                            $curr_user = UserManager::instance()->getUserById($u_id);	
2404
                            if ($curr_user->isActive() || $curr_user->isRestricted()) {
2405
                                $withoutpermissions_concerned_addresses[user_getemail($u_id)] = true;
2406
                            }
2407
                            unset($concerned_ids[$u_id]);
2408
                        } else {
2409
                            $concerned_ids[$u_id] = true;
2410
                        }
2411
                    } else {
2412
                        if (!$address['check_permissions']) {
2413
                            $withoutpermissions_concerned_addresses[$address['address']] = true;
2414
                            unset($concerned_addresses[$address['address']]);
2415
                        } else {
2416
                            $concerned_addresses[$address['address']] = true;
2417
                        }
2418
                    }
2419
                }
2420
            }
2421
        }
2422
        //concerned_ids contains users for wich we have to check permissions
2423
        //concerned_addresses contains emails for which there is no existing user. Permissions will be checked (Anonymous users)
2424
        //withoutpermissions_concerned_addresses contains emails for which there is no permissions check
2425
        
2426
        //Prepare e-mail
2427
        list($host,) = explode(':',$GLOBALS['sys_default_domain']);
0 ignored issues
show
Unused Code introduced by
The assignment to $host is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
2428
2429
        
2430
        //treat anonymous users
2431
        $text_mail = $this->createMailForUsers(array($GLOBALS['UGROUP_ANONYMOUS']),$changes,$group_id,$group_artifact_id,$ok,$subject);
2432
        $html_mail = $this->createHTMLMailForUsers(array($GLOBALS['UGROUP_ANONYMOUS']),$changes,$group_id,$group_artifact_id,$ok,$subject);
2433
2434
        if ($ok) {
2435
            $this->sendNotification(array_keys($concerned_addresses), $subject, $text_mail, $html_mail);
0 ignored issues
show
Bug introduced by
It seems like $text_mail defined by $this->createMailForUser...fact_id, $ok, $subject) on line 2431 can be null; however, Artifact::sendNotification() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $html_mail defined by $this->createHTMLMailFor...fact_id, $ok, $subject) on line 2432 can be null; however, Artifact::sendNotification() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
2436
        }
2437
2438
        //treat 'without permissions' emails
2439
        if (count($withoutpermissions_concerned_addresses)) {
2440
            $text_mail = $this->createMailForUsers(false,$changes,$group_id,$group_artifact_id,$ok,$subject);
2441
            $html_mail = $this->createHTMLMailForUsers(false,$changes,$group_id,$group_artifact_id,$ok,$subject);
2442
       
2443
            if ($ok) {
2444
                $this->sendNotification(array_keys($withoutpermissions_concerned_addresses), $subject, $text_mail, $html_mail);
0 ignored issues
show
Bug introduced by
It seems like $text_mail defined by $this->createMailForUser...fact_id, $ok, $subject) on line 2440 can be null; however, Artifact::sendNotification() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $html_mail defined by $this->createHTMLMailFor...fact_id, $ok, $subject) on line 2441 can be null; however, Artifact::sendNotification() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
2445
            }
2446
2447
        }
2448
        
2449
        //now group other registered users
2450
2451
	    //echo "<br>concerned_ids = ".implode(',',array_keys($concerned_ids));
2452
2453
	    $this->groupNotificationList(array_keys($concerned_ids),$user_sets,$ugroup_sets);
2454
2455
	    //echo "<br>user_sets = "; print_r($user_sets); echo ", ugroup_sets = "; print_r($ugroup_sets);
2456
	    reset($ugroup_sets);
2457
	    while (list($x,$ugroups) = each($ugroup_sets)) {
2458
            unset($arr_addresses);
2459
            
2460
            $user_ids = $user_sets[$x];
2461
            //echo "<br>--->  preparing mail $x for ";print_r($user_ids);
2462
            $text_mail = $this->createMailForUsers($ugroups,$changes,$group_id,$group_artifact_id,$ok,$subject);
2463
            $html_mail = $this->createHTMLMailForUsers($ugroups,$changes,$group_id,$group_artifact_id,$ok,$subject);
2464
            if (!$ok) continue; //don't send the mail if nothing permitted for this user group
2465
2466
            foreach ($user_ids as $user_id) {
2467
                $arr_addresses[] = user_getemail($user_id);
2468
            }
2469
       
2470
            if ($arr_addresses) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arr_addresses 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...
2471
                $this->sendNotification($arr_addresses, $subject, $text_mail, $html_mail);
0 ignored issues
show
Bug introduced by
It seems like $text_mail defined by $this->createMailForUser...fact_id, $ok, $subject) on line 2462 can be null; however, Artifact::sendNotification() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $html_mail defined by $this->createHTMLMailFor...fact_id, $ok, $subject) on line 2463 can be null; however, Artifact::sendNotification() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
2472
            }
2473
	    }
2474
      }
2475
    }
2476
    
2477
    /**
2478
     * Build notification list based on user preferences
2479
     *
2480
     * @param Array                  $addresses
2481
     * @param String                 $subject
2482
     * @param Codendi_Mail_Interface $text_mail
2483
     * @param Codendi_Mail_Interface $html_mail
2484
     */
2485
    function sendNotification($addresses, $subject, $text_mail, $html_mail) {
2486
        $html_addresses = array();
2487
        $text_addresses = array();
2488
        
2489
        $mailMgr   = new MailManager();
2490
        $mailPrefs = $mailMgr->getMailPreferencesByEmail($addresses);
2491
        foreach ($mailPrefs['html'] as $user) {
2492
            $html_addresses[] = $user->getEmail();
2493
        }
2494
        foreach ($mailPrefs['text'] as $user) {
2495
            $text_addresses[] = $user->getEmail();
2496
        }
2497
2498
        $mail = null;
2499
        if ($text_mail && count($text_addresses)) {
2500
            $this->sendMail($text_mail, $subject, $text_addresses);
2501
        }
2502
        if ($html_mail && count($html_addresses)) {
2503
            if ($text_mail) {
2504
                $html_mail->setBodyText($text_mail->getBody());
0 ignored issues
show
Bug introduced by
The method setBodyText() does not exist on Codendi_Mail_Interface. Did you maybe mean setBody()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2505
            }
2506
            $this->sendMail($html_mail, $subject, $html_addresses);
2507
        }
2508
    }
2509
    
2510
    /**
2511
     * Finalize & send mail to peple
2512
     *
2513
     * @param Codendi_Mail_Interface $mail
2514
     * @param String                 $subject
2515
     * @param Array                  $to
2516
     */
2517
    function sendMail(Codendi_Mail_Interface $mail, $subject, array $to) {
2518
        $mail->addAdditionalHeader("X-Codendi-Project",     $this->ArtifactType->getGroup()->getUnixName());
2519
        $mail->addAdditionalHeader("X-Codendi-Artifact",    $this->ArtifactType->getItemName());
2520
        $mail->addAdditionalHeader("X-Codendi-Artifact-ID", $this->getID());
2521
        $mail->setFrom($GLOBALS['sys_noreply']);
2522
        $mail->setTo(join(',', $to));
2523
        $mail->setSubject($subject);
2524
        $mail->send();
2525
    }
2526
    
2527
    /** for a certain set of users being part of the same ugroups
2528
     * create the mail body containing only fields that they have the permission to read
2529
     */
2530
    function createHTMLMailForUsers($ugroups,$changes,$group_id,$group_artifact_id,&$ok,&$subject) {
2531
        global $art_field_fact,$art_fieldset_fact,$Language;
2532
2533
        $artifact_href = get_server_url()."/tracker/?func=detail&aid=".$this->getID()."&atid=$group_artifact_id&group_id=$group_id";
2534
        $used_fields = $art_field_fact->getAllUsedFields();
2535
        $art_fieldset_fact = new ArtifactFieldsetFactory($this->ArtifactType);
2536
        $used_fieldsets = $art_fieldset_fact->getAllFieldSetsContainingUsedFields();
2537
        $ok = false;
2538
2539
        $hp = $this->getHTMLPurifier();
2540
        
2541
        $body = '';
2542
         
2543
        //generate the field permissions (TRACKER_FIELD_READ, TRACKER_FIEDL_UPDATE or nothing)
2544
        //for all fields of this tracker given the $ugroups the user is part of
2545
        $field_perm = false;
2546
        if ($ugroups) {
2547
            $field_perm = $this->ArtifactType->getFieldPermissions($ugroups);
2548
        }
2549
2550
        $summ = "";
2551
        if ($field_perm === false || (isset($field_perm['summary']) && $field_perm['summary'] && permission_can_read_field($field_perm['summary']))) {
2552
            $summ = util_unconvert_htmlspecialchars($this->getValue('summary'));
2553
        }
2554
        $subject='['.$this->ArtifactType->getCapsItemName().' #'.$this->getID().'] '.$summ;
2555
         
2556
2557
        // artifact fields
2558
        // Generate the message preamble with all required
2559
        // artifact fields - Changes first if there are some.
2560
        $body .= '<h1>'. $summ .'</h1>'; 
2561
        if ($changes) {
2562
            $body .= $this->formatChangesHTML($changes, $field_perm, $artifact_href, $visible_change);
2563
            if (!$visible_change) return;
2564
        }
2565
        $ok = true;
2566
        
2567
        // Snapshot
2568
        $fields_per_line=2;
2569
        // the column number is the number of field per line * 2 (label + value)
2570
        // + the number of field per line -1 (a blank column between each pair "label-value" to give more space)
2571
        $columns_number = ($fields_per_line * 2) + ($fields_per_line - 1);
2572
        $max_size=40;
2573
        $snapshot = '';
2574
        foreach($used_fieldsets as $fieldset_id => $result_fieldset) {
2575
2576
            // this variable will tell us if we have to display the fieldset or not (if there is at least one field to display or not)
2577
            $display_fieldset = false;
2578
2579
            $fieldset_html = '';
2580
2581
            $i = 0;
2582
            $fields_in_fieldset = $result_fieldset->getAllUsedFields();
2583
            foreach ($fields_in_fieldset as $key => $field) {
2584
                if ($field->getName() != 'comment_type_id' && $field->getName() != 'artifact_id') {
2585
                    $field_html = $this->_getFieldLabelAndValueForHTMLMail($group_id, $group_artifact_id, $field, $field_perm);
2586
                    if ($field_html) {
2587
2588
                        // if the user can read at least one field, we can display the fieldset this field is within
2589
                        $display_fieldset = true;
2590
2591
                        list($sz,) = explode("/",$field->getDisplaySize());
2592
2593
                        // Details field must be on one row
2594
                        if ($sz > $max_size || $field->getName()=='details') {
2595
                            $fieldset_html .= "\n<TR>".
2596
                                  '<TD align="left" valign="top" width="10%" nowrap="nowrap">'. $field_html['label'] .'</td>'.
2597
                                  '<TD valign="top" width="90%" colspan="'.($columns_number-1).'">'.$field_html['value'].'</TD>'.
2598
                                  "\n</TR>";
2599
                            $i=0;
2600
                        } else {
2601
                            $fieldset_html .= ($i % $fields_per_line ? '':"\n<TR>");
2602
                            $fieldset_html .= '<TD align="left" valign="top" width="10%" nowrap="nowrap">'. $field_html['label'] .'</td>'.
2603
                                              '<TD width="38%" valign="top">'. $field_html['value'] .'</TD>';
2604
                            $i++;
2605
                            // if the line is not full, we add a additional column to give more space
2606
                            $fieldset_html .= ($i % $fields_per_line) ? '<td class="artifact_spacer" width="4%">&nbsp;</td>':"\n</TR>";
2607
                        }
2608
                    }
2609
                }
2610
            }
2611
2612
            // We display the fieldset only if there is at least one field inside that we can display
2613
            if ($display_fieldset) {
2614
                $snapshot .= '<TR style="color: #444444; background-color: #F6F6F6;"><TD COLSPAN="'.(int)$columns_number.'">&nbsp;<span title="'. $hp->purify(SimpleSanitizer::unsanitize($result_fieldset->getDescriptionText()), CODENDI_PURIFIER_CONVERT_HTML) .'">'. $hp->purify(SimpleSanitizer::unsanitize($result_fieldset->getLabel()), CODENDI_PURIFIER_CONVERT_HTML) .'</span></TD></TR>';
2615
                $snapshot .= $fieldset_html;
2616
            }
2617
        }
2618
        if ($snapshot) {
2619
            $body .= '<h2>'.$GLOBALS['Language']->getText('tracker_include_artifact', 'mail_snapshot_title').'</h2>';
2620
            $body .= '<table>';
2621
            $body .= $snapshot;
2622
            $body .= '</table>';
2623
            if (!$changes) {
2624
                $body .= $this->fetchHtmlAnswerButton($artifact_href);
2625
            }
2626
        }
2627
        
2628
        $result = $this->getFollowups();
2629
        if (db_numrows($result)) {
2630
            $body .= '<h2>'.$GLOBALS['Language']->getText('tracker_include_artifact', 'mail_comment_title').'</h2>';
2631
2632
            $body .= '<dl>';
2633
            while ($row = db_fetch_array($result)) {
2634
                $orig_subm = $this->getOriginalCommentSubmitter($row['artifact_history_id']);
2635
                $orig_sub_mod_by = db_result($orig_subm, 0, 'mod_by');
2636
                if ($orig_sub_mod_by == 100) {
2637
                    $submitter_real_name = db_result($orig_subm, 0, 'email');
2638
                } else {
2639
                    $submitter = UserManager::instance()->getUserById($orig_sub_mod_by);
2640
                    $submitter_real_name = $submitter->getRealName();
2641
                }
2642
                
2643
                $orig_date = $this->getOriginalCommentDate($row['artifact_history_id']);
2644
                $subm_date = format_date($GLOBALS['Language']->getText('system', 'datefmt'), db_result($orig_date, 0, 'date'));
2645
                
2646
                $body .= '<dt><strong>'. $submitter_real_name .'</strong> <span style="color:#bbb">'. $subm_date .'</span></dt>';
2647
                $body .= '<dd>'.$this->formatFollowUp($group_id, $row['format'], $row['new_value'], self::OUTPUT_BROWSER).'</dd>';
2648
            }
2649
            $body .= '</dl>';
2650
        }
2651
        // Finaly, transform relatives URLs to absolute 
2652
        // I'm Nicolas Terray and I approve this hack.
2653
        $body = preg_replace('%<a href="/%', '<a href="'.get_server_url().'/', $body);
2654
        
2655
        // Mail is ready, we can create it
2656
        if ($ok) {
2657
            $project = ProjectManager::instance()->getProject($group_id);
2658
            $breadcrumbs = array();
2659
            $breadcrumbs[] = '<a href="'. get_server_url() .'/projects/'. $project->getUnixName($tolower = true) .'" />'. $project->getPublicName() .'</a>';
2660
            $breadcrumbs[] = '<a href="'. get_server_url() .'/tracker/?group_id='. (int)$group_id .'&amp;atid='. (int)$group_artifact_id .'" />'. $hp->purify(SimpleSanitizer::unsanitize($this->ArtifactType->getName())) .'</a>';
2661
            $breadcrumbs[] = '<a href="'. $artifact_href .'" />'. $hp->purify($this->ArtifactType->getItemName().' #'.$this->getID()) .'</a>';
2662
            
2663
            $mail = new Codendi_Mail();
2664
            $mail->getLookAndFeelTemplate()->set('breadcrumbs', $breadcrumbs);
2665
            $mail->getLookAndFeelTemplate()->set('title', $hp->purify($subject, CODENDI_PURIFIER_CONVERT_HTML));
2666
            $mail->getLookAndFeelTemplate()->set('additional_footer_link', '<a href="'. $artifact_href .'">'.$GLOBALS['Language']->getText('tracker_include_artifact', 'mail_direct_link').'</a>');
2667
            $mail->setBodyHtml($body);
2668
            return $mail;
2669
        } else {
2670
            return null;
2671
        }
2672
    }
2673
    
2674
    /**
2675
     * @return string html call to action button to include in an html mail
2676
     */
2677
    public function fetchHtmlAnswerButton($artifact_href) {
2678
        return '<p align="right" class="cta">
2679
            <a href="'. $artifact_href .'" target="_blank">' .
2680
            $GLOBALS['Language']->getText('tracker_include_artifact','mail_answer_now') .
2681
            '</a>
2682
            </p>';
2683
    }
2684
2685
    /**
2686
     * return a field for the given user.
2687
     *
2688
     * @protected
2689
     **/
2690
    function _getFieldLabelAndValueForHTMLMail($group_id, $group_artifact_id, $field, $field_perm) {
2691
        $html = false;
2692
        $read_only = true;
2693
        $field_name = $field->getName();
2694
        if ($field_perm === false || (isset($field_perm[$field_name]) && $field_perm[$field_name] && permission_can_read_field($field_perm[$field_name]))) {
2695
2696
            // For multi select box, we need to retrieve all the values
2697
            if ( $field->isMultiSelectBox() ) {
2698
                $field_value = $field->getValues($this->getID());
2699
            } else {
2700
                $field_value = $this->getValue($field->getName());
2701
            }
2702
2703
            $field_html  = new ArtifactFieldHtml($field);
2704
            $field_html->disableJavascript();
2705
            $label = $field_html->labelDisplay(false,false,false);
2706
2707
            if ($field->getName() == 'submitted_by') {
2708
                $value = util_user_link(user_getname($field_value));
2709
            } else if ($field->getName() == 'open_date') {
2710
                $value = format_date($GLOBALS['Language']->getText('system', 'datefmt'),$field_value);
2711
            } else if ($field->getName() == 'last_update_date') {
2712
                $value = format_date($GLOBALS['Language']->getText('system', 'datefmt'),$field_value);
2713
            } else {
2714
                $value = $field_html->display($this->ArtifactType->getID(), $field_value, false, false, $read_only, false, false, 0, false, 0, false, 0, false);
2715
                $value = util_make_links($value, $group_id, $group_artifact_id);
2716
            }
2717
            $html = array('label' => $label, 'value' => $value);
2718
        }
2719
        return $html;
2720
    }
2721
2722
	/** for a certain set of users being part of the same ugroups
2723
	 * create the mail body containing only fields that they have the permission to read
2724
	 */
2725
	function createMailForUsers($ugroups,$changes,$group_id,$group_artifact_id,&$ok,&$subject) {
2726
	  global $art_field_fact,$art_fieldset_fact,$Language;
2727
2728
	  $fmt_len = 40;
2729
	  $fmt_left = sprintf("%%-%ds ", $fmt_len-1);
2730
	  $fmt_right = "%s";
2731
	  $artifact_href = get_server_url()."/tracker/?func=detail&aid=".$this->getID()."&atid=$group_artifact_id&group_id=$group_id";
2732
	  $used_fields = $art_field_fact->getAllUsedFields();
2733
      $art_fieldset_fact = new ArtifactFieldsetFactory($this->ArtifactType);
2734
      $used_fieldsets = $art_fieldset_fact->getAllFieldSetsContainingUsedFields();
2735
	  $ok = false;
2736
2737
	  $body = '';
2738
	    
2739
	  //generate the field permissions (TRACKER_FIELD_READ, TRACKER_FIEDL_UPDATE or nothing)
2740
	  //for all fields of this tracker given the $ugroups the user is part of
2741
      $field_perm = false;
2742
	  if ($ugroups) {
2743
          $field_perm = $this->ArtifactType->getFieldPermissions($ugroups);
2744
      }
2745
2746
	  $summ = "";
2747
	  if ($field_perm === false || (isset($field_perm['summary']) && $field_perm['summary'] && permission_can_read_field($field_perm['summary']))) {
2748
	    $summ = util_unconvert_htmlspecialchars($this->getValue('summary'));
2749
	  }
2750
	  $subject='['.$this->ArtifactType->getCapsItemName().' #'.$this->getID().'] '.$summ;
2751
	  
2752
2753
	  //echo "<br>......... field_perm for "; print_r($ugroups); echo " = "; print_r($field_perm);
2754
2755
	    // artifact fields
2756
	    // Generate the message preamble with all required
2757
	    // artifact fields - Changes first if there are some.
2758
	    if ($changes) {
2759
		$body = $GLOBALS['sys_lf']."=============   ".strtoupper(SimpleSanitizer::unsanitize($this->ArtifactType->getName()))." #".$this->getID().
2760
		    ": ".$Language->getText('tracker_include_artifact','latest_modif')."   =============". $GLOBALS['sys_lf'] . $artifact_href . $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . 
2761
		  $this->formatChanges($changes,$field_perm,$visible_change) . $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] ."";
2762
2763
		if (!$visible_change) return;
2764
	    }
2765
	    $ok = true;
2766
	    
2767
            
2768
	    $visible_snapshot = false;
2769
	    $full_snapshot = "";
2770
2771
        // We write the name of the project
2772
        $pm = ProjectManager::instance();
2773
        $full_snapshot .= sprintf($fmt_left . $GLOBALS['sys_lf'] ."",$Language->getText('tracker_include_artifact','project').' '.util_unconvert_htmlspecialchars($pm->getProject($group_id)->getPublicName() ));
2774
        
2775
	    // Write all the fields, grouped by fieldsetset and ordered by rank.
2776
	    $left = 1;
2777
	    
2778
	    $visible_fieldset = false;
2779
        // fetch list of used fieldsets for this artifact
2780
	    foreach ($used_fieldsets as $fieldset_id => $fieldset) {
2781
            $fieldset_snapshot = '';
2782
            $used_fields = $fieldset->getAllUsedFields();
2783
            // fetch list of used fields and the current field values
2784
            // for this artifact
2785
            while ( list($key, $field) = each($used_fields) ) {
0 ignored issues
show
Unused Code introduced by
The assignment to $key is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
2786
2787
                $field_name = $field->getName();
2788
2789
                if ($field_perm === false || (isset($field_perm[$field_name]) && $field_perm[$field_name] && permission_can_read_field($field_perm[$field_name]))) {
2790
            
2791
                    $field_html = new ArtifactFieldHtml($field);
2792
                    
2793
                    $visible_fieldset = true;
2794
                    $visible_snapshot = true;
2795
2796
                    // For multi select box, we need to retrieve all the values
2797
                    if ( $field->isMultiSelectBox() ) {
2798
                      $field_value = $field->getValues($this->getID());
2799
                    } else {
2800
                      $field_value = $this->getValue($field->getName());
2801
                    }
2802
                    $display = $field_html->display($group_artifact_id,
2803
                                  $field_value,false,true,true,true);
2804
                    $item = sprintf(($left? $fmt_left : $fmt_right), $display);
2805
                    if (strlen($item) > $fmt_len) {
2806
                        if (! $left) {
2807
                          $fieldset_snapshot .= "". $GLOBALS['sys_lf'] ."";
2808
                        }
2809
                        $fieldset_snapshot .= sprintf($fmt_right, $display);
2810
                        $fieldset_snapshot .= "". $GLOBALS['sys_lf'] ."";
2811
                        $left = 1;
2812
                    } else {
2813
                        $fieldset_snapshot .= $item;
2814
                        $left = ! $left;
2815
                        if ($left) {
2816
                          $fieldset_snapshot .= "". $GLOBALS['sys_lf'] ."";
2817
                        }
2818
                    }
2819
              }
2820
            
2821
            } // while
2822
            
2823
            if ($visible_fieldset) {
2824
                $full_snapshot .= "". $GLOBALS['sys_lf'] ."";
2825
                $full_snapshot .= ($left?"":"". $GLOBALS['sys_lf'] ."");
2826
                $full_snapshot .= '--- '.SimpleSanitizer::unsanitize($fieldset->getLabel()).' ---';
2827
                $full_snapshot .= "". $GLOBALS['sys_lf'] ."";
2828
                $full_snapshot .= $fieldset_snapshot;
2829
            }
2830
        }
2831
2832
	    if ($visible_snapshot) $full_snapshot .= "". $GLOBALS['sys_lf'] ."";
2833
2834
	    $body .= "=============   ".strtoupper(SimpleSanitizer::unsanitize($this->ArtifactType->getName()))." #".$this->getID().
2835
		": ".$Language->getText('tracker_include_artifact','full_snapshot')."   =============". $GLOBALS['sys_lf'] . 
2836
		($changes ? '':$artifact_href) . $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $full_snapshot;
2837
2838
2839
	    if (! $left) {
2840
	      $body .= "". $GLOBALS['sys_lf'] ."";
2841
	    }
2842
	    
2843
	    // Now display other special fields
2844
        
2845
        // Then output the history of bug comments from newest to oldest
2846
	    $body .= $this->showFollowUpComments($group_id, 0, self::OUTPUT_MAIL_TEXT);
2847
	    
2848
	    // Then output the CC list
2849
	    $body .= "". $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $this->showCCList($group_id, $group_artifact_id, true);
2850
	    
2851
	    // Then output the dependencies
2852
	    $body .= "". $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $this->showDependencies($group_id,$group_artifact_id,true);
2853
	    
2854
	    // Then output the history of attached files from newest to oldest
2855
	    $body .= "". $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $this->showAttachedFiles($group_id,$group_artifact_id,true);
2856
	    
2857
        // Extract references from the message
2858
        $referenceManager =& ReferenceManager::instance();
2859
        $ref_array = $referenceManager->extractReferencesGrouped($body, $group_id);
2860
        if (count($ref_array) > 0) {
2861
            $body .= $GLOBALS['sys_lf'].$GLOBALS['sys_lf'].$Language->getText('tracker_include_artifact','references').$GLOBALS['sys_lf'];
2862
        }
2863
        foreach ($ref_array as $description => $match_array) {
2864
            $body .= $GLOBALS['sys_lf'].$description.":".$GLOBALS['sys_lf'];
2865
            foreach ($match_array as $match => $ref_instance) {
2866
                $reference =& $ref_instance->getReference();
2867
                $body .= ' '.$ref_instance->getMatch().': '.$ref_instance->getFullGotoLink().$GLOBALS['sys_lf'];
2868
            }
2869
        }
2870
2871
        // Finally output the message trailer
2872
        $body .= "". $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . $Language->getText('tracker_include_artifact','follow_link');
2873
        $body .= "". $GLOBALS['sys_lf'] . $artifact_href;
2874
2875
        if ($ok) {
2876
            $mail = new Mail();
2877
            $mail->setBody($body);
2878
            return $mail;
2879
        } else {
2880
            return null;
2881
        }
2882
	}
2883
2884
    /**
2885
     * Check whether $field_name is readable according to $field_perm
2886
     *
2887
     * All permissions are granted when $field_perm is equal to false.
2888
     * $field_perm is equal to false when addresses are added in tracker admin to
2889
     * "Global Email Notification with "check permission" unchecked.
2890
     *
2891
     * @param Array $field_perm ...
2892
     *
2893
     * @return Boolean
2894
     */
2895
    public function hasFieldPermission($field_perm, $field_name) {
2896
        $hasPerm = false;
2897
        if ($field_perm === false) {
2898
            $hasPerm = true;
2899
        } else {
2900
            if (isset($field_perm[$field_name]) && $field_perm[$field_name] && permission_can_read_field($field_perm[$field_name])) {
2901
                $hasPerm = true;
2902
            }
2903
        }
2904
        return $hasPerm;
2905
    }
2906
2907
    /**
2908
    * Format the changes
2909
    *
2910
    * @param changes: array of changes
2911
    * @param $field_perm an array with the permission associated to each field. false to no check perms
2912
    * @param $visible_change only needed when using permissions. Returns true if there is any change 
2913
    * that the user has permission to see
2914
    *
2915
    * @return string
2916
    */
2917
    function formatChanges($changes,$field_perm,&$visible_change) {
2918
    
2919
        global $art_field_fact,$Language;
2920
        $visible_change = false;
2921
        $out_hdr = '';
2922
        $out = '';
2923
        $out_com = '';
2924
        $out_att = '';
2925
        reset($changes);
2926
        $fmt = "%20s | %-25s | %s".$GLOBALS['sys_lf'];
2927
    
2928
    
2929
        if ($this->hasFieldPermission($field_perm, 'assigned_to') ||
2930
            $this->hasFieldPermission($field_perm, 'multi_assigned_to') ||
2931
            (!isset($field_perm['assigned_to']) && !isset($field_perm['multi_assigned_to']))) {
2932
               if (user_isloggedin()) {
2933
                      $user_id = user_getid();
2934
                      $out_hdr = $Language->getText('tracker_include_artifact','changes_by').' '.user_getrealname($user_id).' <'.user_getemail($user_id).">". $GLOBALS['sys_lf'] ."";
2935
                      $out_hdr .= $Language->getText('tracker_import_utils','date').': '.format_date($GLOBALS['Language']->getText('system', 'datefmt'),time()).' ('.user_get_timezone().')';
2936
               } else {
2937
                      $out_hdr = $Language->getText('tracker_include_artifact','changes_by').' '.$Language->getText('tracker_include_artifact','anon_user').'        '.$Language->getText('tracker_import_utils','date').': '.format_date($GLOBALS['Language']->getText('system', 'datefmt'),time());
2938
               }
2939
        }
2940
        //Process special cases first: follow-up comment
2941
        if (array_key_exists('comment', $changes) && $changes['comment']) {
2942
          $visible_change = true;
2943
          $out_com = $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] ."---------------   ".$Language->getText('tracker_include_artifact','add_flup_comment')."   ----------------". $GLOBALS['sys_lf'] ."";
2944
        
2945
          if (isset($changes['comment']['type']) && $changes['comment']['type'] != $Language->getText('global','none') && $changes['comment']['type'] != '') {
2946
                 $out_com .= "[".$changes['comment']['type']."]".$GLOBALS['sys_lf'];
2947
          }
2948
          $out_com .= $this->formatFollowUp(null, $changes['comment']['format'], $changes['comment']['add'], self::OUTPUT_MAIL_TEXT);
2949
          unset($changes['comment']);
2950
        }
2951
        
2952
           //Process special cases first: file attachment
2953
        if (array_key_exists('attach', $changes) && $changes['attach']) {
2954
          $visible_change = true;
2955
          $out_att = "". $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] ."---------------    ".$Language->getText('tracker_include_artifact','add_attachment')."     -----------------". $GLOBALS['sys_lf'] ."";
2956
          $out_att .= sprintf($Language->getText('tracker_include_artifact','file_name')." %-30s ".$Language->getText('tracker_include_artifact','size').":%d KB". $GLOBALS['sys_lf'] ."",$changes['attach']['name'],
2957
         intval($changes['attach']['size']/1024) );
2958
          $out_att .= $changes['attach']['description'] . $GLOBALS['sys_lf'] . $changes['attach']['href'];
2959
          unset($changes['attach']);
2960
        }
2961
    
2962
        // All the rest of the fields now
2963
        reset($changes);
2964
        
2965
        while ( list($field_name,$h) = each($changes)) {
2966
            // If both removed and added items are empty skip - Sanity check
2967
            if (((isset($h['del']) && $h['del']) || (isset($h['add']) && $h['add']))
2968
                && $this->hasFieldPermission($field_perm, $field_name)) {
2969
2970
                $visible_change = true;
2971
                $label = $field_name;
2972
                $field = $art_field_fact->getFieldFromName($field_name);
2973
                if ( $field ) {
2974
                    $label = $field->getLabel();
2975
                    if (isset($h['del'])) {
2976
                        $h['del'] = SimpleSanitizer::unsanitize(util_unconvert_htmlspecialchars($h['del']));
2977
                    }
2978
                    if (isset($h['add'])) {
2979
                        $h['add'] = SimpleSanitizer::unsanitize(util_unconvert_htmlspecialchars($h['add']));
2980
                    }
2981
                }
2982
                $out .= sprintf($fmt, SimpleSanitizer::unsanitize($label), isset($h['del'])?$h['del']:"",isset($h['add'])?$h['add']:"");
2983
            }
2984
        } // while
2985
    
2986
        if ($out) {
2987
            $out = $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] . sprintf($fmt,$Language->getText('tracker_include_artifact','what').'    ',$Language->getText('tracker_include_artifact','removed'),$Language->getText('tracker_include_artifact','added')).
2988
                "------------------------------------------------------------------". $GLOBALS['sys_lf'] . $out;
2989
            }
2990
    
2991
        return($out_hdr.$out.$out_com.$out_att);	    
2992
    }
2993
2994
    /**
2995
     * Format the changes
2996
     *
2997
     * @param changes: array of changes
2998
     * @param $field_perm an array with the permission associated to each field. false to no check perms
2999
     * @param string $artifact_href The direct link to the artifact
3000
     * @param $visible_change only needed when using permissions. Returns true if there is any change
3001
     * that the user has permission to see
3002
     *
3003
     * @return string
3004
     */
3005
    function formatChangesHTML($changes, $field_perm, $artifact_href, &$visible_change) {
3006
        
3007
        global $art_field_fact,$Language;
3008
        $group_id = $this->ArtifactType->getGroupID();
3009
        $visible_change = false;
3010
        $out = '';
3011
        $out_com = '';
3012
        $out_ch  = '';
3013
        reset($changes);
3014
        $fmt = "%20s | %-25s | %s".$GLOBALS['sys_lf'];
3015
3016
        $hp = $this->getHTMLPurifier();
3017
3018
        
3019
        $out .= '<h2>'.$Language->getText('tracker_include_artifact','mail_latest_modifications').'</h2>';
3020
        $out .= '
3021
            <div class="tracker_artifact_followup_header">
3022
                <div class="tracker_artifact_followup_title">
3023
                    <span class="tracker_artifact_followup_title_user">';
3024
3025
        $user = UserManager::instance()->getCurrentUser();
3026
3027
        if ($this->hasFieldPermission($field_perm, 'assigned_to') || 
3028
            $this->hasFieldPermission($field_perm, 'multi_assigned_to') || 
3029
            (!isset($field_perm['assigned_to']) && !isset($field_perm['multi_assigned_to']))) {
3030
            if ($user->isLoggedIn()) {
3031
                $out .= '<a href="mailto:'.$hp->purify($user->getEmail()).'">'.$hp->purify($user->getRealName()).' ('.$hp->purify($user->getUserName()) .')</a>';
3032
            } else {
3033
                $out = $Language->getText('tracker_include_artifact','anon_user');
3034
            }
3035
        }
3036
        
3037
        $timezone = '';
3038
        if ($user->getId() != 0) {
3039
            $timezone = ' ('.$user->getTimezone().')';
3040
        }
3041
        
3042
        $out .= '
3043
                    </span>
3044
                </div>
3045
                <div class="tracker_artifact_followup_date">'. format_date($GLOBALS['Language']->getText('system', 'datefmt'), $_SERVER['REQUEST_TIME']).$timezone.'</div>
3046
            </div>
3047
            <div class="tracker_artifact_followup_avatar">
3048
                '. $user->fetchHtmlAvatar() .'
3049
            </div>
3050
            <div class="tracker_artifact_followup_content">
3051
                <div class="tracker_artifact_followup_comment">';
3052
        
3053
        //Process special cases first: follow-up comment
3054
        if (!empty($changes['comment'])) {
3055
            $visible_change = true;
3056
            if (!empty($changes['comment']['type']) && $changes['comment']['type'] != $Language->getText('global','none')) {
3057
                $out_com .= "<strong>[". $changes['comment']['type'] ."]</strong><br />";
3058
            }
3059
            $out_com .= '<div class="tracker_artifact_followup_comment_body">';
3060
            $out_com .= $this->formatFollowUp($group_id, $changes['comment']['format'], $changes['comment']['add'], self::OUTPUT_BROWSER);
3061
            $out_com .= '</div>';
3062
            unset($changes['comment']);
3063
        }
3064
        //Process special cases first: file attachment
3065
        if (!empty($changes['attach'])) {
3066
            $visible_change = true;
3067
            $out_ch .= '<tr>';
3068
            $out_ch .= '<td valign="top"><strong>'. $Language->getText('tracker_include_artifact','add_attachment') .'</strong></td>';
3069
            $out_ch .= '<td valign="top"><a href="'.$changes['attach']['href'].'">'.$hp->purify($changes['attach']['name']).'</a> ('.size_readable($changes['attach']['size']).')</td>';
3070
            $out_ch .= '</tr>';
3071
            unset($changes['attach']);
3072
        }
3073
3074
        // All the rest of the fields now
3075
        reset($changes);
3076
        foreach ($changes as $field_name => $h) {
3077
            // If both removed and added items are empty skip - Sanity check
3078
            if ((!empty($h['del']) || !empty($h['add'])) && $this->hasFieldPermission($field_perm, $field_name)) {
3079
                $visible_change = true;
3080
                $label          = $field_name;
3081
                $field          = $art_field_fact->getFieldFromName($field_name);
3082
                if ( $field ) {
3083
                    $label = $field->getLabel();
3084
                    if (isset($h['del'])) {
3085
                        $h['del'] = SimpleSanitizer::unsanitize(util_unconvert_htmlspecialchars($h['del']));
3086
                    }
3087
                    if (isset($h['add'])) {
3088
                        $h['add'] = SimpleSanitizer::unsanitize(util_unconvert_htmlspecialchars($h['add']));
3089
                    }
3090
                }
3091
                $out_ch .= '<tr>';
3092
                $out_ch .= '  <td valign="top" nowrap="nowrap"><ul style="margin:0; padding:0; margin-left:1.5em; "><li><strong>'.$hp->purify(SimpleSanitizer::unsanitize($label)).':&nbsp;</strong></li></ul></td>';
3093
                $out_ch .= '  <td valign="top">';
3094
                if ($field && ($field->getDisplayType() == 'TA' || $field->getDisplayType() == 'TF')) {
3095
                    $before = explode("\n", $h['del']);
3096
                    $after  = explode("\n", $h['add']);
3097
                    $callback = array(Codendi_HTMLPurifier::instance(), 'purify');
3098
                    $d = new Codendi_Diff(
3099
                        array_map($callback, $before, array_fill(0, count($before), CODENDI_PURIFIER_CONVERT_HTML)),
3100
                        array_map($callback, $after,  array_fill(0, count($after),  CODENDI_PURIFIER_CONVERT_HTML))
3101
                    );
3102
                    $f = new Codendi_HtmlUnifiedDiffFormatter(2);
3103
                    $diff = $f->format($d);
3104
                    if ($diff) {
3105
                        $out_ch .= '<div class="diff">'. $diff .'</div>';
3106
                    }
3107
                } else {
3108
                    $before = '<del>'.$hp->purify($h['del']).'</del>';
3109
                    $after  = '<ins>'.$hp->purify($h['add']).'</ins>';
3110
                    if ($field && $field->getDisplayType() == 'MB') {
3111
                        if (strlen($before) != 11) { //'<del></del>' => empty
3112
                            $out_ch .= $before;
3113
                        }
3114
                        if (strlen($before) != 11 && strlen($after) != 11) { //'<ins></ins>' => empty
3115
                            $out_ch .= ' &plusmn; ';
3116
                        }
3117
                        if (strlen($after) != 11) { //'<ins></ins>' => empty
3118
                            $out_ch .= $after;
3119
                        }
3120
                    } else {
3121
                        $out_ch .= $before;
3122
                        $out_ch .= ' &rarr; ';
3123
                        $out_ch .= $after;
3124
                    }
3125
                }
3126
                $out_ch .= '</td>';
3127
                $out_ch .= '</tr>';
3128
            }
3129
        }
3130
        if ($out_ch) {
3131
            $out_ch = '<div class="tracker_artifact_followup_comment_changes">' .
3132
                $Language->getText('tracker_include_artifact','mail_changes').'<table cellpadding="0" border="0" cellspacing="0" class="artifact_changes">' .
3133
                $out_ch .
3134
                '</table>
3135
                </div>';
3136
        }
3137
        
3138
        $out .= $out_com . $out_ch;
3139
        
3140
        $out .= '
3141
                </div>
3142
            </div>
3143
            <div style="clear:both;"></div>';
3144
        $out .= $this->fetchHtmlAnswerButton($artifact_href);
3145
        return $out;
3146
    }
3147
3148
        
3149
        /**
3150
         * Return the string to display the follow ups comments 
3151
         *
3152
         * @param Integer   group_id: the group id
3153
         * @param Integer   output By default set to OUTPUT_BROWSER, the output is displayed on browser 
3154
         *                         set to OUTPUT_MAIL_TEXT, the followups will be sent in mail 
3155
         *                         else is an export csv/DB
3156
         * @return string the follow-up comments to display in HTML or in ascii mode
3157
         */
3158
        function showFollowUpComments($group_id, $pv, $output = self::OUTPUT_BROWSER) {
3159
            $hp = $this->getHTMLPurifier();
3160
            $uh = UserHelper::instance();
3161
3162
            //
3163
            //  Format the comment rows from artifact_history
3164
            //  
3165
            global $Language;
3166
            
3167
                //$group = $this->ArtifactType->getGroup();
3168
                $group_artifact_id = $this->ArtifactType->getID();
3169
                //$group_id = $group->getGroupId();
3170
3171
            $result=$this->getFollowups ();
3172
            $rows=db_numrows($result);
3173
        
3174
            // No followup comment -> return now
3175
            if ($rows <= 0) {
3176
                        if ($output == self::OUTPUT_EXPORT || $output == self::OUTPUT_MAIL_TEXT)
3177
                            $out = $GLOBALS['sys_lf'].$GLOBALS['sys_lf']." ".$Language->getText('tracker_import_utils','no_followups').$GLOBALS['sys_lf'];
3178
                        else
3179
                            $out = '<H4>'.$Language->getText('tracker_import_utils','no_followups').'</H4>';
3180
                        return $out;
3181
            }
3182
        
3183
            $out = '';
3184
            
3185
            // Header first
3186
            if ($output == self::OUTPUT_EXPORT || $output == self::OUTPUT_MAIL_TEXT) {
3187
                $out .= $Language->getText('tracker_include_artifact','follow_ups').$GLOBALS['sys_lf'].str_repeat("*",strlen($Language->getText('tracker_include_artifact','follow_ups')));
3188
            } else {
3189
                if ($rows > 0) {
3190
                    $out .= '<div style="text-align:right">';
3191
                    $out .= '<script type="text/javascript">
3192
                    function tracker_expand_all_comments() {
3193
                        $H(tracker_comment_togglers).values().each(function (value) {
3194
                                (value)(null, true, true);
3195
                        });
3196
                    }
3197
                    
3198
                    function tracker_collapse_all_comments() {
3199
                        $H(tracker_comment_togglers).values().each(function (value) {
3200
                                (value)(null, true, false);
3201
                        });
3202
                    }
3203
                    var matches = location.hash.match(/#comment_(\d*)/);
3204
                    var linked_comment_id = matches ? matches[1] : null;
3205
                    </script>';
3206
                    $out .= '<a href="#expand_all" onclick="tracker_expand_all_comments(); return false;">'.
3207
                    	$Language->getText('tracker_include_artifact','expand_all').
3208
                    	'</a> | <a href="#expand_all" onclick="tracker_collapse_all_comments(); return false;">'.
3209
                    	$Language->getText('tracker_include_artifact','collapse_all').'</a></div>';
3210
                }
3211
            }
3212
            
3213
            // Loop throuh the follow-up comments and format them
3214
            $last_visit_date = user_get_preference('tracker_'. $this->ArtifactType->getId() .'_artifact_'. $this->getId() .'_last_visit');
3215
            for ($i=0; $i < $rows; $i++) {
3216
                $comment_type = db_result($result, $i, 'comment_type');
3217
                $comment_type_id = db_result($result, $i, 'comment_type_id');
3218
                $comment_id = db_result($result, $i, 'artifact_history_id');
3219
                $field_name = db_result($result, $i, 'field_name');
3220
                $orig_subm = $this->getOriginalCommentSubmitter($comment_id);
3221
                $orig_date = $this->getOriginalCommentDate($comment_id);
3222
                $value = db_result($result, $i, 'new_value');
3223
                $isHtml = db_result($result, $i, 'format');
3224
                
3225
                
3226
                if ( ($comment_type_id == 100) ||($comment_type == "") ) {
3227
                    $comment_type = '';
3228
                } else {
3229
                    $comment_type = '['.SimpleSanitizer::unsanitize($comment_type).']';
3230
                }
3231
                
3232
                if ($output == self::OUTPUT_EXPORT || $output == self::OUTPUT_MAIL_TEXT) {
3233
                    $fmt = $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] ."------------------------------------------------------------------". $GLOBALS['sys_lf'].
3234
                        $Language->getText('tracker_import_utils','date').": %-30s".$Language->getText('global','by').": %s". $GLOBALS['sys_lf'] ."%s";
3235
                    //The mail body
3236
                    $comment_txt = $this->formatFollowUp($group_id, $isHtml, $value, $output); 
3237
                    $out .= sprintf($fmt,
3238
                                    format_date(util_get_user_preferences_export_datefmt(),db_result($orig_date, 0, 'date')),
3239
                                    (db_result($orig_subm, 0, 'mod_by')==100?db_result($orig_subm, 0, 'email'):user_getname(db_result($orig_subm, 0, 'mod_by'))),
3240
                                    ($comment_type != '' ? $comment_type.$GLOBALS['sys_lf'] : '') . $comment_txt
3241
                                    );
3242
                } else {
3243
                    $style = '';
3244
                    $toggle = 'ic/toggle_minus.png';
3245
                    if ($last_visit_date > db_result($orig_date, 0, 'date') && $i > 0) {
3246
                        $style = 'style="display:none;"';
3247
                        $toggle = 'ic/toggle_plus.png';
3248
                    }
3249
                    $out .= "\n".'
3250
                    <div class="followup_comment" id="comment_'. $comment_id .'">
3251
                        <div class="'. util_get_alt_row_color($i) .' followup_comment_header">
3252
                            <div class="followup_comment_title">';
3253
                    $out .= '<script type="text/javascript">document.write(\'<span>';
3254
                    $out .= $GLOBALS['HTML']->getImage(
3255
                        $toggle, 
3256
                        array(
3257
                            'id' => 'comment_'. (int)$comment_id .'_toggle', 
3258
                            'style' => 'vertical-align:middle; cursor:hand; cursor:pointer;',
3259
                            'title' => addslashes($GLOBALS['Language']->getText('tracker_include_artifact', 'toggle'))
3260
                        )
3261
                    );
3262
                    $out .= '</span>\');</script>';
3263
                    $out .= '<script type="text/javascript">';
3264
                    $out .= "tracker_comment_togglers[". (int)$comment_id ."] = function (evt, force, expand) {
3265
                        var toggle = $('comment_". (int)$comment_id ."_toggle');
3266
                        var element = $('comment_". (int)$comment_id ."_content');
3267
                        if (element) {
3268
                            if (!force || (expand && !element.visible()) || (!expand && element.visible())) {
3269
                                Element.toggle(element);
3270
                                
3271
                                //replace image
3272
                                var src_search = 'toggle_minus';
3273
                                var src_replace = 'toggle_plus';
3274
                                if (toggle.src.match('toggle_plus')) {
3275
                                    src_search = 'toggle_plus';
3276
                                    src_replace = 'toggle_minus';
3277
                                }
3278
                                toggle.src = toggle.src.replace(src_search, src_replace);
3279
                            }
3280
                        }
3281
                        if (evt) {
3282
                            Event.stop(evt);
3283
                        }
3284
                        return false;
3285
                    };
3286
                    Event.observe($('comment_". (int)$comment_id ."_toggle'), 'click', tracker_comment_togglers[". (int)$comment_id ."]);";
3287
                    $out .= '</script>';
3288
                    $out .= '<span><a href="#comment_'. (int)$comment_id .'" title="Link to this comment - #'. (int)$comment_id .'" onclick="tracker_comment_togglers['. (int)$comment_id .'](null, true, true);">';
3289
                    $out .= $GLOBALS['HTML']->getImage('ic/comment.png', array('border' => 0, 'style' => 'vertical-align:middle', 'title' => 'Link to this comment - #'. (int)$comment_id));
3290
                    $out .= '</a> </span>';
3291
                    $out .= '<span class="followup_comment_title_user">';
3292
                    if (db_result($orig_subm, 0, 'mod_by')==100) {
3293
                        $out .= db_result($orig_subm, 0, 'email');
3294
                    } else {
3295
                        $out .= '<a href="/users/'.urlencode(user_getname(db_result($orig_subm, 0, 'mod_by'))).'">'. $hp->purify($uh->getDisplayNameFromUserId(db_result($orig_subm, 0, 'mod_by')), CODENDI_PURIFIER_CONVERT_HTML) .'</a>';
3296
                    }
3297
                    
3298
                    $out .= ' </span>';
3299
                    $out .= '<span class="followup_comment_title_date">';
3300
                    $out .= html_time_ago(db_result($orig_date, 0, 'date'));
3301
                    $out .= '</span>';
3302
                    if ($field_name != "comment") {
3303
                        $out .= "  (".$GLOBALS['Language']->getText('tracker_include_artifact','last_edited')." ";
3304
                        $out .= '<span class="followup_comment_title_edited_user">';
3305
                        if (db_result($result, $i, 'mod_by')==100) {
3306
                            $out .= db_result($result, $i, 'email');
3307
                        } else {
3308
                            $out .= '<a href="/users/'.urlencode(user_getname(db_result($result, $i, 'mod_by'))).'">'. $hp->purify(user_getname(db_result($result, $i, 'mod_by')), CODENDI_PURIFIER_CONVERT_HTML) .'</a>';
3309
                        }
3310
                        $out .= ' </span>';
3311
                        $out .= '<span class="followup_comment_title_date">';
3312
                        $out .= html_time_ago(db_result($result, $i, 'date'));
3313
                        $out .= '</span>'.")";
3314
                    }
3315
                    $out .= "\n</div><!-- followup_comment_title -->\n";
3316
                    $out .= '<div class="followup_comment_title_toolbar">';
3317
                    if (db_result($orig_subm, 0, 'mod_by')==100) {
3318
                        $user_quoted = db_result($orig_subm, 0, 'email');
3319
                    } else {
3320
                        $user_quoted = $uh->getDisplayNameFromUserId(db_result($orig_subm, 0, 'mod_by'));
3321
                    }
3322
                    $user_quoted = addslashes(addslashes($user_quoted));
3323
                    if ($pv == 0) {
3324
                        $out .= '<script type="text/javascript">document.write(\'<a href="#quote" onclick="tracker_quote_comment(\\\''. $user_quoted .'\\\', \\\''. (int)$comment_id .'\\\'); return false;" title="quote">';
3325
                        $out .= $GLOBALS['HTML']->getImage('ic/quote.png', array('border' => 0, 'alt' => 'quote'));
3326
                        $out .= '</a>\');</script>';
3327
                    }
3328
                    if ($this->userCanEditFollowupComment($comment_id) && !$pv) {
3329
                        $out .= '<a href="/tracker/?func=editcomment&group_id='.(int)$group_id.'&aid='.(int)$this->getID().'&atid='.(int)$group_artifact_id.'&artifact_history_id='.(int)$comment_id.'" title="'. $GLOBALS['Language']->getText('tracker_fieldeditor','edit').'">';
3330
                        $out .= $GLOBALS['HTML']->getImage('ic/edit.png', array('border' => 0, 'alt' => $GLOBALS['Language']->getText('tracker_fieldeditor','edit')));
3331
                        $out .= '</a>';
3332
                        $out .= '<a href="/tracker/?func=delete_comment&group_id='.(int)$group_id.'&aid='.(int)$this->getID().'&atid='.(int)$group_artifact_id.'&artifact_history_id='.(int)$comment_id.'" ';
3333
                        $out .= ' onClick="return confirm(\''. $GLOBALS['Language']->getText('tracker_include_artifact','delete_comment') .'\')" title="'. $GLOBALS['Language']->getText('tracker_include_artifact','del') .'">';
3334
                        $out .= $GLOBALS['HTML']->getImage('ic/close.png', array('border' => 0, 'alt' => $GLOBALS['Language']->getText('tracker_include_artifact','del')));
3335
                        $out .= '</a>';
3336
                    }
3337
                    $out .= "\n</div><!-- followup_comment_title_toolbar -->\n";
3338
                    $out .= '<div style="clear:both;"></div>';
3339
                    $out .= "\n</div><!-- followup_comment_header -->\n";
3340
                    $out .= '<div class="followup_comment_content" '. $style .' id="comment_'. (int)$comment_id .'_content">';
3341
                    if ($comment_type != "") {
3342
                        $out .= '<div class="followup_comment_content_type"><b>'.  $hp->purify($comment_type, CODENDI_PURIFIER_CONVERT_HTML)  .'</b></div>';
3343
                    }
3344
                    $out .= $this->formatFollowUp($group_id, $isHtml, $value, $output); 
3345
                    $out .= '</div>';
3346
                    $out .= '</div>';
3347
                    $out .= '<script type="text/javascript">
3348
                    if (linked_comment_id == '. (int)$comment_id .') {
3349
                        tracker_comment_togglers['. (int)$comment_id .'](null, true, true);
3350
                    }
3351
                    </script>';
3352
                }
3353
            }
3354
            if ($output == self::OUTPUT_BROWSER) {
3355
                if ($rows > 0) {
3356
                    $out .= '<div style="text-align:right">';
3357
                    $out .= '<a href="#expand_all" onclick="tracker_expand_all_comments(); return false;">'.
3358
                    $Language->getText('tracker_include_artifact','expand_all').
3359
                    '</a> | <a href="#expand_all" onclick="tracker_collapse_all_comments(); return false;">'.
3360
                    $Language->getText('tracker_include_artifact','collapse_all').'</a></div>';
3361
                }
3362
            }
3363
        
3364
            // final touch...
3365
            $out .= (($output != self::OUTPUT_BROWSER) ? $GLOBALS['sys_lf'] : "");
3366
        
3367
            return($out);
3368
                
3369
        }
3370
3371
                /**
3372
         * Display the list of CC addresses
3373
         *
3374
         * @param group_id: the group id
3375
         * @param group_artifact_id: the artifact type ID
3376
         * @param ascii: ascii mode
3377
         *
3378
         * @return void
3379
         */
3380
        function showCCList ($group_id, $group_artifact_id, $ascii=false, $pv = 0) {
3381
            $hp = Codendi_HTMLPurifier::instance();
3382
            global $Language;
3383
        
3384
            //
3385
            //      format the CC list for this artifact
3386
            //
3387
        
3388
            $result = $this->getCCList();
3389
            $rows   = db_numrows($result);
3390
            $out    = '';
3391
            
3392
            // Nobody in the CC list -> return now
3393
            if ($rows <= 0) {
3394
                        if ($ascii)
3395
                            $out = $Language->getText('tracker_include_artifact','cc_empty').$GLOBALS['sys_lf'];
3396
                        else
3397
                            $out = '<H4>'.$Language->getText('tracker_include_artifact','cc_empty').'</H4>';
3398
                        return $out;
3399
            }
3400
        
3401
            // Header first an determine what the print out format is
3402
            // based on output type (Ascii, HTML)
3403
            if ($ascii) {
3404
		$out .= $Language->getText('tracker_include_artifact','cc_list').$GLOBALS['sys_lf'].str_repeat("*",strlen($Language->getText('tracker_include_artifact','cc_list'))).$GLOBALS['sys_lf'].$GLOBALS['sys_lf'];
3405
                        $fmt = "%-35s | %s".$GLOBALS['sys_lf'];
3406
                        $out .= sprintf($fmt, $Language->getText('tracker_include_artifact','cc_address'), $Language->getText('tracker_include_artifact','fill_cc_list_cmt'));
3407
                        $out .= "------------------------------------------------------------------". $GLOBALS['sys_lf'];
3408
            } else {    
3409
        
3410
                        $title_arr=array();
3411
                        $title_arr[]=$Language->getText('tracker_include_artifact','cc_address');
3412
                        $title_arr[]=$Language->getText('tracker_include_artifact','fill_cc_list_cmt');
3413
                        $title_arr[]=$Language->getText('tracker_include_artifact','added_by');
3414
                        $title_arr[]=$Language->getText('tracker_include_artifact','posted_on');
3415
                        if ($pv == 0) {
3416
                            $title_arr[]=$Language->getText('tracker_include_canned','delete');
3417
                        }
3418
                        $out .= html_build_list_table_top ($title_arr);
3419
                
3420
                        $fmt = "\n".'<TR class="%s"><td>%s</td><td>%s</td><td align="center">%s</td><td align="center">%s</td>';
3421
                        if ($pv == 0) {
3422
                            $fmt .= '<td align="center">%s</td>';
3423
                        }
3424
                        $fmt .= '</tr>';
3425
                }
3426
                
3427
            // Loop through the cc and format them
3428
            for ($i=0; $i < $rows; $i++) {
3429
        
3430
                        $email = db_result($result, $i, 'email');
3431
                        $artifact_cc_id = db_result($result, $i, 'artifact_cc_id');
3432
                
3433
                        // if the CC is a user point to its user page else build a mailto: URL
3434
                        $res_username = user_get_result_set_from_unix($email);
3435
                        if ($res_username && (db_numrows($res_username) == 1))
3436
                            $href_cc = util_user_link($email);
3437
                        else
3438
                            $href_cc = '<a href="mailto:'.util_normalize_email($email).'">'.$email.'</a>';
3439
                
3440
                        if ($ascii) {
3441
                            $out .= sprintf($fmt, $email, SimpleSanitizer::unsanitize(db_result($result, $i, 'comment')));
3442
                        } else {
3443
                
3444
                            // show CC delete icon if one of the condition is met:
3445
                            // (a) current user is a group member
3446
                            // (b) the CC name is the current user 
3447
                            // (c) the CC email address matches the one of the current user
3448
                            // (d) the current user is the person who added a gieven name in CC list
3449
                            if ( user_ismember($this->ArtifactType->getGroupID()) ||
3450
                                (user_getname(user_getid()) == $email) ||  
3451
                                (user_getemail(user_getid()) == $email) ||
3452
                                (user_getname(user_getid()) == db_result($result, $i, 'user_name') )) {
3453
                                        $html_delete = '<a href="?func=delete_cc&group_id='.(int)$group_id.'&aid='.(int)$this->getID().'&atid='.(int)$group_artifact_id.'&artifact_cc_id='.(int)$artifact_cc_id.'" '.
3454
                                        ' onClick="return confirm(\''.$Language->getText('tracker_include_artifact','delete_cc').'\')">'.
3455
                                        '<IMG SRC="'.util_get_image_theme("ic/trash.png").'" HEIGHT="16" WIDTH="16" BORDER="0" ALT="'.$Language->getText('global','btn_delete').'"></A>';
3456
                            } else {
3457
                                        $html_delete = '-';
3458
                            }
3459
                
3460
                            $out .= sprintf($fmt,
3461
                                            util_get_alt_row_color($i),
3462
                                            $href_cc,
3463
                                            $hp->purify(SimpleSanitizer::unsanitize(db_result($result, $i, 'comment')), CODENDI_PURIFIER_BASIC, $this->ArtifactType->getGroupId()) ,
3464
                                            util_user_link(db_result($result, $i, 'user_name')),
3465
                                            format_date($GLOBALS['Language']->getText('system', 'datefmt'),db_result($result, $i, 'date')),
3466
                                            $html_delete);
3467
                        
3468
                        } // for
3469
            }
3470
        
3471
            // final touch...
3472
            $out .= ($ascii ? $GLOBALS['sys_lf'] : "</TABLE>");
3473
        
3474
            return($out);
3475
        
3476
        }
3477
3478
                /**
3479
         * Display the artifact dependencies list
3480
         *
3481
         * @param group_id: the group id
3482
         * @param group_artifact_id: the artifact type ID
3483
         * @param ascii: ascii mode
3484
         *
3485
         * @return void
3486
         */
3487
        function showDependencies ($group_id, $group_artifact_id, $ascii=false, $pv = 0) {
3488
            $hp = Codendi_HTMLPurifier::instance();
3489
            global $Language;
3490
        
3491
            //
3492
            //      format the dependencies list for this artifact
3493
            //
3494
        
3495
            $result=$this->getDependencies();
3496
            $rows=db_numrows($result);
3497
            $out = '';
3498
            // Nobody in the dependencies list -> return now
3499
            if ($rows <= 0) {
3500
                        if ($ascii)
3501
                            $out = $Language->getText('tracker_include_artifact','dep_list_empty').$GLOBALS['sys_lf'];
3502
                        else
3503
                            $out = '<H4>'.$Language->getText('tracker_include_artifact','dep_list_empty').'</H4>';
3504
                        return $out;
3505
            }
3506
        
3507
            // Header first an determine what the print out format is
3508
            // based on output type (Ascii, HTML)
3509
            if ($ascii) {
3510
		$out .= $Language->getText('tracker_include_artifact','dep_list').$GLOBALS['sys_lf'].str_repeat("*",strlen($Language->getText('tracker_include_artifact','dep_list'))). $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'];
3511
                        $fmt = "%-15s | %s (%s)". $GLOBALS['sys_lf'];
3512
                        $out .= sprintf($fmt, 
3513
                                        $Language->getText('tracker_include_artifact','artifact'), 
3514
                                        $Language->getText('tracker_include_artifact','summary'),
3515
                                        $Language->getText('global','status')
3516
                        );
3517
                        $out .= "------------------------------------------------------------------". $GLOBALS['sys_lf'];
3518
            } else {    
3519
        
3520
                        $title_arr=array();
3521
                        $title_arr[]=$Language->getText('tracker_include_artifact','artifact');
3522
                        $title_arr[]=$Language->getText('tracker_include_artifact','summary');
3523
                        $title_arr[]=$Language->getText('global','status');
3524
                        $title_arr[]=$Language->getText('tracker_import_admin','tracker');
3525
                        $title_arr[]=$Language->getText('tracker_include_artifact','group');
3526
                        if ($pv == 0) {
3527
                            $title_arr[]=$Language->getText('tracker_include_canned','delete');
3528
                        }
3529
                        $out .= html_build_list_table_top ($title_arr);
3530
                
3531
                        $fmt = "\n".'<TR class="%s"><td>%s</td><td>%s</td><td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>';
3532
                        if ($pv == 0) {
3533
                            $fmt .= '<td align="center">%s</td>';
3534
                        }
3535
                        $fmt .= '</tr>';
3536
                }
3537
                
3538
            // Loop through the denpendencies and format them
3539
            for ($i=0; $i < $rows; $i++) {
3540
        
3541
                        $dependent_on_artifact_id = db_result($result, $i, 'is_dependent_on_artifact_id');
3542
                        $summary = db_result($result, $i, 'summary');
3543
                        $status = db_result($result, $i, 'status');
3544
                        $tracker_label = db_result($result, $i, 'name');
3545
                        $group_label = db_result($result, $i, 'group_name');
3546
                
3547
                        if ($ascii) {
3548
                            $out .= sprintf($fmt, $dependent_on_artifact_id, util_unconvert_htmlspecialchars($summary), $status);
3549
                        } else {
3550
                
3551
                            if ( user_ismember($this->ArtifactType->getGroupID()) ) {
3552
                                        $html_delete = '<a href="?func=delete_dependent&group_id='.(int)$group_id.'&aid='.(int)$this->getID().'&atid='.(int)$group_artifact_id.'&dependent_on_artifact_id='.(int)$dependent_on_artifact_id.'" '.
3553
                                        ' onClick="return confirm(\''.$Language->getText('tracker_include_artifact','del_dep').'\')">'.
3554
                                        '<IMG SRC="'.util_get_image_theme("ic/trash.png").'" HEIGHT="16" WIDTH="16" BORDER="0" ALT="'.$Language->getText('global','btn_delete').'"></A>';
3555
                            } else {
3556
                                        $html_delete = '-';
3557
                            }
3558
                
3559
                            $out .= sprintf($fmt,
3560
                                            util_get_alt_row_color($i),
3561
                                            '<a href="/tracker/?func=gotoid&group_id='.(int)$group_id.'&aid='.(int)$dependent_on_artifact_id.'">'.(int)$dependent_on_artifact_id.'</a>',
3562
                                            $hp->purify(util_unconvert_htmlspecialchars($summary), CODENDI_PURIFIER_CONVERT_HTML) ,
3563
                                            $hp->purify($status, CODENDI_PURIFIER_CONVERT_HTML) ,
3564
                                            $hp->purify(SimpleSanitizer::unsanitize($tracker_label), CODENDI_PURIFIER_CONVERT_HTML) ,
3565
                                            $hp->purify(util_unconvert_htmlspecialchars($group_label), CODENDI_PURIFIER_CONVERT_HTML) ,
3566
                                            $html_delete);
3567
                        
3568
                        } // for
3569
            }
3570
        
3571
            // final touch...
3572
            $out .= ($ascii ? $GLOBALS['sys_lf'] : "</TABLE>");
3573
        
3574
            return($out);
3575
        
3576
        }
3577
3578
                /**
3579
         * Display the list of attached files
3580
         *
3581
         * @param group_id: the group id
3582
         * @param group_artifact_id: the artifact type ID
3583
         * @param ascii: ascii mode
3584
         *
3585
         * @return void
3586
         */
3587
        function showAttachedFiles ($group_id,$group_artifact_id,$ascii=false, $pv = 0) {
3588
        
3589
            global $Language;
3590
            $hp = $this->getHtmlPurifier();
3591
            //
3592
            //  show the files attached to this artifact
3593
            //   
3594
        
3595
            $result=$this->getAttachedFiles();
3596
            $rows=db_numrows($result);
3597
        
3598
            // No file attached -> return now
3599
            if ($rows <= 0) {
3600
                        if ($ascii)
3601
                            $out = $Language->getText('tracker_include_artifact','no_file_attached').$GLOBALS['sys_lf'];
3602
                        else
3603
                            $out = '<H4>'.$Language->getText('tracker_include_artifact','no_file_attached').'</H4>';
3604
                        return $out;
3605
                }
3606
                
3607
            // Header first
3608
            if ($ascii) {
3609
		$out = $Language->getText('tracker_include_artifact','file_attachment').$GLOBALS['sys_lf'].str_repeat("*",strlen($Language->getText('tracker_include_artifact','file_attachment')));
3610
            } else {    
3611
                
3612
                $title_arr=array();
3613
                $title_arr[]=$Language->getText('tracker_include_artifact','name');
3614
                $title_arr[]=$Language->getText('tracker_include_artifact','desc');
3615
                $title_arr[]=$Language->getText('tracker_include_artifact','size_kb');
3616
                $title_arr[]=$Language->getText('global','by');
3617
                $title_arr[]=$Language->getText('tracker_include_artifact','posted_on');
3618
                if ($pv == 0) {
3619
                    $title_arr[]=$Language->getText('tracker_include_canned','delete');
3620
                }
3621
        
3622
                $out = html_build_list_table_top ($title_arr);
3623
            }
3624
        
3625
            // Determine what the print out format is based on output type (Ascii, HTML)
3626
            if ($ascii) {
3627
                        $fmt = $GLOBALS['sys_lf'] . $GLOBALS['sys_lf'] ."------------------------------------------------------------------". $GLOBALS['sys_lf'].
3628
                            $Language->getText('tracker_import_utils','date').": %s  ".$Language->getText('tracker_include_artifact','name').": %s  ".$Language->getText('tracker_include_artifact','size').": %dKB   ".$Language->getText('global','by').": %s". $GLOBALS['sys_lf'] ."%s". $GLOBALS['sys_lf'] ."%s";
3629
            } else {
3630
                        $fmt = "". $GLOBALS['sys_lf'] . '<TR class="%s"><td>%s</td><td>%s</td><td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>';
3631
                        if ($pv == 0) {
3632
                            $fmt .= '<td align="center">%s</td>';
3633
                        }
3634
                        $fmt .= '</tr>';
3635
            }
3636
        
3637
            // Determine which protocl to use for embedded URL in ASCII format
3638
            $server=get_server_url();
3639
        
3640
            // Loop throuh the attached files and format them
3641
            for ($i=0; $i < $rows; $i++) {
3642
        
3643
                        $artifact_file_id = db_result($result, $i, 'id');
3644
                        $href = "/tracker/download.php?artifact_id=".(int)$this->getID()."&id=".(int)$artifact_file_id;
3645
                
3646
                        if ($ascii) {
3647
                            $out .= sprintf($fmt,
3648
                                            format_date($GLOBALS['Language']->getText('system', 'datefmt'),db_result($result, $i, 'adddate')),
3649
                                             db_result($result, $i, 'filename') ,
3650
                                            intval(db_result($result, $i, 'filesize')/1024),
3651
                                             db_result($result, $i, 'user_name'),
3652
                                             SimpleSanitizer::unsanitize(db_result($result, $i, 'description')),
3653
                                            $server.$href);
3654
                        } else {
3655
                            // show CC delete icon if one of the condition is met:
3656
                            // (a) current user is group member
3657
                            // (b) the current user is the person who added a gieven name in CC list
3658
			  if ( user_ismember($this->ArtifactType->getGroupID()) ||
3659
                                (user_getname(user_getid()) == db_result($result, $i, 'user_name') )) {
3660
                                        $html_delete = '<a href="?func=delete_file&group_id='.(int)$group_id."&atid=".(int)$group_artifact_id."&aid=".(int)$this->getID()."&id=".(int)db_result($result, $i, 'id').'" '.
3661
                                            ' onClick="return confirm(\''.$Language->getText('tracker_include_artifact','delete_attachment').'\')">'.
3662
                                            '<IMG SRC="'.util_get_image_theme("ic/trash.png").'" HEIGHT="16" WIDTH="16" BORDER="0" ALT="'.$Language->getText('global','btn_delete').'"></A>';
3663
                            } else {
3664
                                        $html_delete = '-';
3665
                            }
3666
                            $out .= sprintf($fmt,
3667
                                            util_get_alt_row_color($i),
3668
                                            '<a href="'.$href.'">'.  $hp->purify(db_result($result, $i, 'filename'), CODENDI_PURIFIER_CONVERT_HTML) .'</a>',
3669
                                             $hp->purify(SimpleSanitizer::unsanitize(db_result($result, $i, 'description')), CODENDI_PURIFIER_BASIC, $group_id) ,
3670
                                            intval(db_result($result, $i, 'filesize')/1024),
3671
                                            util_user_link(db_result($result, $i, 'user_name')),
3672
                                            format_date($GLOBALS['Language']->getText('system', 'datefmt'),db_result($result, $i, 'adddate')),
3673
                                            $html_delete);
3674
                        }
3675
        } // for
3676
        
3677
            // final touch...
3678
            $out .= ($ascii ? "". $GLOBALS['sys_lf'] ."" : "</TABLE>");
3679
        
3680
            return($out);
3681
        
3682
        }
3683
3684
    /** Update the last_update_date field in the Artifact table to 'now'
3685
     */
3686
    function update_last_update_date() {
3687
        $sql="UPDATE artifact SET last_update_date=".time().
3688
            " WHERE artifact_id=". db_ei($this->getID()) ;
3689
                
3690
        return db_query($sql);        
3691
    }
3692
    
3693
    /**
3694
     * Returns an instance of Codendi_HTMLPurifier
3695
     *
3696
     * @return Codendi_HTMLPurifier
3697
     */
3698
    public function getHTMLPurifier() {
3699
        return Codendi_HTMLPurifier::instance();
3700
    }
3701
3702
    /**
3703
     * Format the comment text to a given format according to parameters
3704
     *
3705
     * @param Integer $groupId       Project id
3706
     * @param Boolean $commentFormat $value's format
3707
     * @param String  $value         Comment content
3708
     * @param Boolean $output        Output format
3709
     *
3710
     * @return String
3711
     */
3712
    public function formatFollowUp($groupId, $commentFormat, $value, $output) {
3713
        $commentText = '';
3714
        if ($output == self::OUTPUT_EXPORT) {
3715
            return util_unconvert_htmlspecialchars($value);
3716
        } else {
3717
            $hp = $this->getHTMLPurifier();
3718
            if ($output == self::OUTPUT_MAIL_TEXT) {
3719
                if ($commentFormat == self::FORMAT_HTML){
3720
                    $commentText = $hp->purify(util_unconvert_htmlspecialchars($value), CODENDI_PURIFIER_STRIP_HTML);
3721
                } else {
3722
                    $commentText = $value;
3723
                }
3724
                $commentText = util_unconvert_htmlspecialchars($commentText);
3725
            } else {
3726
                if ($commentFormat == self::FORMAT_HTML) {
3727
                    $level = CODENDI_PURIFIER_LIGHT;
3728
                } else {
3729
                    $level = CODENDI_PURIFIER_BASIC;
3730
                }
3731
                $commentText =  $hp->purify(util_unconvert_htmlspecialchars($value), $level, $groupId);
3732
            }
3733
            return $commentText;
3734
        }
3735
    }
3736
3737
}
3738
3739
?>
3740