2 # MantisBT - A PHP based bugtracking system
4 # MantisBT is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 2 of the License, or
7 # (at your option) any later version.
9 # MantisBT is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
21 * @subpackage BugnoteAPI
22 * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
23 * @copyright Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net
24 * @link http://www.mantisbt.org
26 * @uses access_api.php
27 * @uses authentication_api.php
29 * @uses bug_revision_api.php
30 * @uses config_api.php
31 * @uses constant_inc.php
32 * @uses database_api.php
36 * @uses helper_api.php
37 * @uses history_api.php
40 * @uses utility_api.php
43 require_api( 'access_api.php' );
44 require_api( 'authentication_api.php' );
45 require_api( 'bug_api.php' );
46 require_api( 'bug_revision_api.php' );
47 require_api( 'config_api.php' );
48 require_api( 'constant_inc.php' );
49 require_api( 'database_api.php' );
50 require_api( 'email_api.php' );
51 require_api( 'error_api.php' );
52 require_api( 'event_api.php' );
53 require_api( 'helper_api.php' );
54 require_api( 'history_api.php' );
55 require_api( 'lang_api.php' );
56 require_api( 'user_api.php' );
57 require_api( 'utility_api.php' );
60 * Bugnote Data Structure Definition
78 * Check if a bugnote with the given ID exists
79 * return true if the bugnote exists, false otherwise
80 * @param int $p_bugnote_id bugnote id
84 function bugnote_exists( $p_bugnote_id ) {
85 $c_bugnote_id = db_prepare_int( $p_bugnote_id );
86 $t_bugnote_table = db_get_table( 'bugnote' );
88 $query = "SELECT COUNT(*)
90 WHERE id=" . db_param();
91 $result = db_query_bound( $query, Array( $c_bugnote_id ) );
93 if( 0 == db_result( $result ) ) {
101 * Check if a bugnote with the given ID exists
102 * return true if the bugnote exists, raise an error if not
103 * @param int $p_bugnote_id bugnote id
106 function bugnote_ensure_exists( $p_bugnote_id ) {
107 if( !bugnote_exists( $p_bugnote_id ) ) {
108 trigger_error( ERROR_BUGNOTE_NOT_FOUND
, ERROR
);
113 * Check if the given user is the reporter of the bugnote
114 * return true if the user is the reporter, false otherwise
115 * @param int $p_bugnote_id bugnote id
116 * @param int $p_user_id user id
120 function bugnote_is_user_reporter( $p_bugnote_id, $p_user_id ) {
121 if( bugnote_get_field( $p_bugnote_id, 'reporter_id' ) == $p_user_id ) {
129 * Add a bugnote to a bug
130 * return the ID of the new bugnote
131 * @param int $p_bug_id bug id
132 * @param string $p_bugnote_text bugnote text
133 * @param string $p_time_tracking hh:mm string
134 * @param bool $p_private whether bugnote is private
135 * @param int $p_type bugnote type
136 * @param string $p_attr
137 * @param int $p_user_id user id
138 * @param bool $p_send_email generate email?
139 * @return false|int false or indicating bugnote id added
142 function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE ) {
143 $c_bug_id = db_prepare_int( $p_bug_id );
144 $c_time_tracking = helper_duration_to_minutes( $p_time_tracking );
145 $c_private = db_prepare_bool( $p_private );
146 $c_type = db_prepare_int( $p_type );
148 $t_bugnote_text_table = db_get_table( 'bugnote_text' );
149 $t_bugnote_table = db_get_table( 'bugnote' );
151 $t_time_tracking_enabled = config_get( 'time_tracking_enabled' );
152 $t_time_tracking_without_note = config_get( 'time_tracking_without_note' );
154 if( ON
== $t_time_tracking_enabled && $c_time_tracking > 0 ) {
155 if( is_blank( $p_bugnote_text ) && OFF
== $t_time_tracking_without_note ) {
156 error_parameters( lang_get( 'bugnote' ) );
157 trigger_error( ERROR_EMPTY_FIELD
, ERROR
);
159 $c_type = TIME_TRACKING
;
160 } else if( is_blank( $p_bugnote_text ) ) {
164 $t_bugnote_text = $p_bugnote_text;
167 $t_bugnote_text = event_signal( 'EVENT_BUGNOTE_DATA', $t_bugnote_text, $c_bug_id );
169 # insert bugnote text
170 $query = 'INSERT INTO ' . $t_bugnote_text_table . ' ( note ) VALUES ( ' . db_param() . ' )';
171 db_query_bound( $query, Array( $t_bugnote_text ) );
173 # retrieve bugnote text id number
174 $t_bugnote_text_id = db_insert_id( $t_bugnote_text_table );
176 # get user information
177 if( $p_user_id === null ) {
178 $c_user_id = auth_get_current_user_id();
180 $c_user_id = db_prepare_int( $p_user_id );
183 # Check for private bugnotes.
184 if( $p_private && access_has_bug_level( config_get( 'set_view_status_threshold' ), $p_bug_id, $c_user_id ) ) {
185 $t_view_state = VS_PRIVATE
;
187 $t_view_state = VS_PUBLIC
;
190 # insert bugnote info
191 $query = "INSERT INTO $t_bugnote_table
192 (bug_id, reporter_id, bugnote_text_id, view_state, date_submitted, last_modified, note_type, note_attr, time_tracking )
194 (" . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ', ' . db_param() . ' )';
195 db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, db_now(), db_now(), $c_type, $p_attr, $c_time_tracking ) );
198 $t_bugnote_id = db_insert_id( $t_bugnote_table );
200 # update bug last updated
201 bug_update_date( $p_bug_id );
204 history_log_event_special( $p_bug_id, BUGNOTE_ADDED
, bugnote_format_id( $t_bugnote_id ) );
206 # if it was FEEDBACK its NEW_ now
207 if ( bug_get_field( $p_bug_id, 'status' ) == config_get( 'bug_feedback_status' ) && bug_get_field( $p_bug_id, 'reporter_id' ) == $c_user_id ) {
208 if ( bug_get_field( $p_bug_id, 'handler_id') == 0 ) {
209 bug_set_field( $p_bug_id, 'status', config_get( 'bug_submit_status' ) );
211 bug_set_field( $p_bug_id, 'status', config_get( 'bug_assigned_status' ) );
216 event_signal( 'EVENT_BUGNOTE_ADD', array( $p_bug_id, $t_bugnote_id ) );
218 # only send email if the text is not blank, otherwise, it is just recording of time without a comment.
219 if( TRUE == $p_send_email && !is_blank( $t_bugnote_text ) ) {
220 email_bugnote_add( $p_bug_id );
223 return $t_bugnote_id;
228 * @param int $p_bugnote_id bug note id
232 function bugnote_delete( $p_bugnote_id ) {
233 $c_bugnote_id = db_prepare_int( $p_bugnote_id );
234 $t_bug_id = bugnote_get_field( $p_bugnote_id, 'bug_id' );
235 $t_bugnote_text_id = bugnote_get_field( $p_bugnote_id, 'bugnote_text_id' );
236 $t_bugnote_text_table = db_get_table( 'bugnote_text' );
237 $t_bugnote_table = db_get_table( 'bugnote' );
240 $query = 'DELETE FROM ' . $t_bugnote_table . ' WHERE id=' . db_param();
241 db_query_bound( $query, Array( $c_bugnote_id ) );
243 # Remove the bugnote text
244 $query = 'DELETE FROM ' . $t_bugnote_text_table . ' WHERE id=' . db_param();
245 db_query_bound( $query, Array( $t_bugnote_text_id ) );
247 # log deletion of bug
248 history_log_event_special( $t_bug_id, BUGNOTE_DELETED
, bugnote_format_id( $p_bugnote_id ) );
254 * delete all bugnotes associated with the given bug
255 * @param int $p_bug_id bug id
259 function bugnote_delete_all( $p_bug_id ) {
260 $c_bug_id = db_prepare_int( $p_bug_id );
261 $t_bugnote_table = db_get_table( 'bugnote' );
262 $t_bugnote_text_table = db_get_table( 'bugnote_text' );
264 # Delete the bugnote text items
265 $query = "SELECT bugnote_text_id
266 FROM $t_bugnote_table
267 WHERE bug_id=" . db_param();
268 $result = db_query_bound( $query, Array( $c_bug_id ) );
269 $bugnote_count = db_num_rows( $result );
270 for( $i = 0;$i < $bugnote_count;$i++
) {
271 $row = db_fetch_array( $result );
272 $t_bugnote_text_id = $row['bugnote_text_id'];
274 # Delete the corresponding bugnote texts
275 $query = "DELETE FROM $t_bugnote_text_table
276 WHERE id=" . db_param();
277 db_query_bound( $query, Array( $t_bugnote_text_id ) );
280 # Delete the corresponding bugnotes
281 $query = "DELETE FROM $t_bugnote_table
282 WHERE bug_id=" . db_param();
283 $result = db_query_bound( $query, Array( $c_bug_id ) );
285 # db_query errors on failure so:
290 * Get the text associated with the bugnote
291 * @param int $p_bugnote_id bugnote id
292 * @return string bugnote text
295 function bugnote_get_text( $p_bugnote_id ) {
296 $t_bugnote_text_id = bugnote_get_field( $p_bugnote_id, 'bugnote_text_id' );
297 $t_bugnote_text_table = db_get_table( 'bugnote_text' );
299 # grab the bugnote text
300 $query = "SELECT note
301 FROM $t_bugnote_text_table
302 WHERE id=" . db_param();
303 $result = db_query_bound( $query, Array( $t_bugnote_text_id ) );
305 return db_result( $result );
309 * Get a field for the given bugnote
310 * @param int $p_bugnote_id bugnote id
311 * @param string $p_field_name field name
312 * @return string field value
315 function bugnote_get_field( $p_bugnote_id, $p_field_name ) {
316 global $g_cache_bugnote;
318 if( isset( $g_cache_bugnote[(int)$p_bugnote_id] ) ) {
319 return $g_cache_bugnote[(int)$p_bugnote_id]->$p_field_name;
322 $c_bugnote_id = db_prepare_int( $p_bugnote_id );
323 $c_field_name = db_prepare_string( $p_field_name );
324 $t_bugnote_table = db_get_table( 'bugnote' );
326 $query = "SELECT $c_field_name
327 FROM $t_bugnote_table
328 WHERE id=" . db_param();
329 $result = db_query_bound( $query, Array( $c_bugnote_id ), 1 );
331 return db_result( $result );
335 * Get latest bugnote id
336 * @param int $p_bug_id bug id
337 * @return int latest bugnote id
340 function bugnote_get_latest_id( $p_bug_id ) {
341 $c_bug_id = db_prepare_int( $p_bug_id );
342 $t_bugnote_table = db_get_table( 'bugnote' );
345 FROM $t_bugnote_table
346 WHERE bug_id=" . db_param() . "
347 ORDER by last_modified DESC";
348 $result = db_query_bound( $query, Array( $c_bug_id ), 1 );
350 return (int)db_result( $result );
354 * Build the bugnotes array for the given bug_id filtered by specified $p_user_access_level.
355 * Bugnotes are sorted by date_submitted according to 'bugnote_order' configuration setting.
356 * Return BugnoteData class object with raw values from the tables except the field
357 * last_modified - it is UNIX_TIMESTAMP.
358 * @param int $p_bug_id bug id
359 * @param int $p_user_bugnote_order sort order
360 * @param int $p_user_bugnote_limit number of bugnotes to display to user
361 * @param int $p_user_id user id
362 * @return array array of bugnotes
365 function bugnote_get_all_visible_bugnotes( $p_bug_id, $p_user_bugnote_order, $p_user_bugnote_limit, $p_user_id = null ) {
366 if( $p_user_id === null ) {
367 $t_user_id = auth_get_current_user_id();
369 $t_user_id = $p_user_id;
372 $t_project_id = bug_get_field( $p_bug_id, 'project_id' );
373 $t_user_access_level = user_get_access_level( $t_user_id, $t_project_id );
375 $t_all_bugnotes = bugnote_get_all_bugnotes( $p_bug_id );
376 $t_private_bugnote_threshold = config_get( 'private_bugnote_threshold' );
378 $t_private_bugnote_visible = access_compare_level( $t_user_access_level, config_get( 'private_bugnote_threshold' ) );
379 $t_time_tracking_visible = access_compare_level( $t_user_access_level, config_get( 'time_tracking_view_threshold' ) );
381 $t_bugnotes = array();
382 $t_bugnote_count = count( $t_all_bugnotes );
383 $t_bugnote_limit = $p_user_bugnote_limit > 0 ?
$p_user_bugnote_limit : $t_bugnote_count;
384 $t_bugnotes_found = 0;
386 # build a list of the latest bugnotes that the user can see
387 for ( $i = 0; ( $i < $t_bugnote_count ) && ( $t_bugnotes_found < $t_bugnote_limit ); $i++
) {
388 $t_bugnote = array_pop( $t_all_bugnotes );
390 if( $t_private_bugnote_visible ||
$t_bugnote->reporter_id
== $t_user_id ||
( VS_PUBLIC
== $t_bugnote->view_state
) ) {
392 # If the access level specified is not enough to see time tracking information
393 # then reset it to 0.
394 if( !$t_time_tracking_visible ) {
395 $t_bugnote->time_tracking
= 0;
398 $t_bugnotes[$t_bugnotes_found++
] = $t_bugnote;
402 # reverse the list for users with ascending view preferences
403 if ( 'ASC' == $p_user_bugnote_order ) {
404 $t_bugnotes = array_reverse( $t_bugnotes );
411 * Build the bugnotes array for the given bug_id.
412 * Return BugnoteData class object with raw values from the tables except the field
413 * last_modified - it is UNIX_TIMESTAMP.
414 * The data is not filtered by VIEW_STATE !!
415 * @param int $p_bug_id bug id
416 * @return array array of bugnotes
419 function bugnote_get_all_bugnotes( $p_bug_id ) {
420 global $g_cache_bugnotes, $g_cache_bugnote;
422 if( !isset( $g_cache_bugnotes ) ) {
423 $g_cache_bugnotes = array();
426 if( !isset( $g_cache_bugnote ) ) {
427 $g_cache_bugnote = array();
430 # the cache should be aware of the sorting order
431 if( !isset( $g_cache_bugnotes[(int)$p_bug_id] ) ) {
432 $t_bugnote_table = db_get_table( 'bugnote' );
433 $t_bugnote_text_table = db_get_table( 'bugnote_text' );
435 # sort by bugnote id which should be more accurate than submit date, since two bugnotes
436 # may be submitted at the same time if submitted using a script (eg: MantisConnect).
437 $t_query = "SELECT b.*, t.note
438 FROM $t_bugnote_table b
439 LEFT JOIN $t_bugnote_text_table t ON b.bugnote_text_id = t.id
440 WHERE b.bug_id=" . db_param() . '
442 $t_bugnotes = array();
444 # BUILD bugnotes array
445 $t_result = db_query_bound( $t_query, array( $p_bug_id ) );
447 while( $row = db_fetch_array( $t_result ) ) {
448 $t_bugnote = new BugnoteData
;
450 $t_bugnote->id
= $row['id'];
451 $t_bugnote->bug_id
= $row['bug_id'];
452 $t_bugnote->note
= $row['note'];
453 $t_bugnote->view_state
= $row['view_state'];
454 $t_bugnote->reporter_id
= $row['reporter_id'];
455 $t_bugnote->date_submitted
= $row['date_submitted'];
456 $t_bugnote->last_modified
= $row['last_modified'];
457 $t_bugnote->note_type
= $row['note_type'];
458 $t_bugnote->note_attr
= $row['note_attr'];
459 $t_bugnote->time_tracking
= $row['time_tracking'];
461 $t_bugnotes[] = $t_bugnote;
462 $g_cache_bugnote[(int)$t_bugnote->id
] = $t_bugnote;
465 $g_cache_bugnotes[(int)$p_bug_id] = $t_bugnotes;
468 return $g_cache_bugnotes[(int)$p_bug_id];
472 * Update the time_tracking field of the bugnote
473 * @param int $p_bugnote_id bugnote id
474 * @param string $p_time_tracking timetracking string (hh:mm format)
478 function bugnote_set_time_tracking( $p_bugnote_id, $p_time_tracking ) {
479 $c_bugnote_id = db_prepare_int( $p_bugnote_id );
480 $c_bugnote_time_tracking = helper_duration_to_minutes( $p_time_tracking );
481 $t_bugnote_table = db_get_table( 'bugnote' );
483 $query = "UPDATE $t_bugnote_table
484 SET time_tracking = " . db_param() . "
485 WHERE id=" . db_param();
486 db_query_bound( $query, Array( $c_bugnote_time_tracking, $c_bugnote_id ) );
488 # db_query errors if there was a problem so:
493 * Update the last_modified field of the bugnote
494 * @param int $p_bugnote_id bugnote id
498 function bugnote_date_update( $p_bugnote_id ) {
499 $c_bugnote_id = db_prepare_int( $p_bugnote_id );
500 $t_bugnote_table = db_get_table( 'bugnote' );
502 $query = "UPDATE $t_bugnote_table
503 SET last_modified=" . db_param() . "
504 WHERE id=" . db_param();
505 db_query_bound( $query, Array( db_now(), $c_bugnote_id ) );
507 # db_query errors if there was a problem so:
512 * Set the bugnote text
513 * @param int $p_bugnote_id bugnote id
514 * @param string $p_bugnote_text bugnote text
518 function bugnote_set_text( $p_bugnote_id, $p_bugnote_text ) {
519 $t_old_text = bugnote_get_text( $p_bugnote_id );
521 if ( $t_old_text == $p_bugnote_text ) {
525 $t_bug_id = bugnote_get_field( $p_bugnote_id, 'bug_id' );
526 $t_bugnote_text_id = bugnote_get_field( $p_bugnote_id, 'bugnote_text_id' );
527 $t_bugnote_text_table = db_get_table( 'bugnote_text' );
529 # insert an 'original' revision if needed
530 if ( bug_revision_count( $t_bug_id, REV_BUGNOTE
, $p_bugnote_id ) < 1 ) {
531 $t_user_id = bugnote_get_field( $p_bugnote_id, 'reporter_id' );
532 $t_timestamp = bugnote_get_field( $p_bugnote_id, 'last_modified' );
533 bug_revision_add( $t_bug_id, $t_user_id, REV_BUGNOTE
, $t_old_text, $p_bugnote_id, $t_timestamp );
536 $query = "UPDATE $t_bugnote_text_table
537 SET note=" . db_param() . " WHERE id=" . db_param();
538 db_query_bound( $query, Array( $p_bugnote_text, $t_bugnote_text_id ) );
540 # updated the last_updated date
541 bugnote_date_update( $p_bugnote_id );
543 # insert a new revision
544 $t_user_id = auth_get_current_user_id();
545 $t_revision_id = bug_revision_add( $t_bug_id, $t_user_id, REV_BUGNOTE
, $p_bugnote_text, $p_bugnote_id );
548 history_log_event_special( $t_bug_id, BUGNOTE_UPDATED
, bugnote_format_id( $p_bugnote_id ), $t_revision_id );
554 * Set the view state of the bugnote
555 * @param int $p_bugnote_id bugnote id
556 * @param bool $p_private
560 function bugnote_set_view_state( $p_bugnote_id, $p_private ) {
561 $c_bugnote_id = db_prepare_int( $p_bugnote_id );
562 $t_bug_id = bugnote_get_field( $p_bugnote_id, 'bug_id' );
565 $t_view_state = VS_PRIVATE
;
567 $t_view_state = VS_PUBLIC
;
570 $t_bugnote_table = db_get_table( 'bugnote' );
572 $query = "UPDATE $t_bugnote_table
573 SET view_state=" . db_param() . "
574 WHERE id=" . db_param();
575 db_query_bound( $query, Array( $t_view_state, $c_bugnote_id ) );
577 history_log_event_special( $t_bug_id, BUGNOTE_STATE_CHANGED
, $t_view_state, bugnote_format_id( $p_bugnote_id ) );
583 * Pad the bugnote id with the appropriate number of zeros for printing
584 * @param int $p_bugnote_id bugnote id
588 function bugnote_format_id( $p_bugnote_id ) {
589 $t_padding = config_get( 'display_bugnote_padding' );
591 return utf8_str_pad( $p_bugnote_id, $t_padding, '0', STR_PAD_LEFT
);
595 * Returns an array of bugnote stats
596 * @param int $p_bug_id bug id
597 * @param string $p_from Starting date (yyyy-mm-dd) inclusive, if blank, then ignored.
598 * @param string $p_to Ending date (yyyy-mm-dd) inclusive, if blank, then ignored.
599 * @return array array of bugnote stats
602 function bugnote_stats_get_events_array( $p_bug_id, $p_from, $p_to ) {
603 $c_bug_id = db_prepare_int( $p_bug_id );
604 $c_to = strtotime( $p_to, SECONDS_PER_DAY
- 1 ); // @23:59:59
605 $c_from = strtotime( $p_from );
607 $t_user_table = db_get_table( 'user' );
608 $t_bugnote_table = db_get_table( 'bugnote' );
610 if( !is_blank( $c_from ) ) {
611 $t_from_where = " AND bn.date_submitted >= $c_from ";
616 if( !is_blank( $c_to ) ) {
617 $t_to_where = " AND bn.date_submitted <= $c_to ";
622 $t_results = array();
624 $query = "SELECT username, SUM(time_tracking) AS sum_time_tracking
625 FROM $t_user_table u, $t_bugnote_table bn
626 WHERE u.id = bn.reporter_id AND
627 bn.bug_id = '$c_bug_id'
628 $t_from_where $t_to_where
629 GROUP BY u.id, u.username";
631 $result = db_query( $query );
633 while( $row = db_fetch_array( $result ) ) {
641 * Returns an array of bugnote stats
642 * @param int $p_project_id project id
643 * @param string $p_from Starting date (yyyy-mm-dd) inclusive, if blank, then ignored.
644 * @param string $p_to Ending date (yyyy-mm-dd) inclusive, if blank, then ignored.
645 * @param int $p_cost cost
646 * @return array array of bugnote stats
649 function bugnote_stats_get_project_array( $p_project_id, $p_from, $p_to, $p_cost ) {
650 $c_project_id = db_prepare_int( $p_project_id );
652 $c_to = strtotime( $p_to, SECONDS_PER_DAY
- 1); // @23:59:59
653 $c_from = strtotime( $p_from );
655 if ( $c_to === false ||
$c_from === false ) {
656 error_parameters( array( $p_form, $p_to ) );
657 trigger_error( ERROR_GENERIC
, ERROR
);
660 $c_cost = db_prepare_double( $p_cost );
662 $t_bug_table = db_get_table( 'bug' );
663 $t_user_table = db_get_table( 'user' );
664 $t_bugnote_table = db_get_table( 'bugnote' );
666 if( !is_blank( $c_from ) ) {
667 $t_from_where = " AND bn.date_submitted >= $c_from";
672 if( !is_blank( $c_to ) ) {
673 $t_to_where = " AND bn.date_submitted <= $c_to";
678 if( ALL_PROJECTS
!= $c_project_id ) {
679 $t_project_where = " AND b.project_id = '$c_project_id' AND bn.bug_id = b.id ";
681 $t_project_where = '';
684 $t_results = array();
686 $query = "SELECT username, summary, bn.bug_id, SUM(time_tracking) AS sum_time_tracking
687 FROM $t_user_table u, $t_bugnote_table bn, $t_bug_table b
688 WHERE u.id = bn.reporter_id AND bn.time_tracking != 0 AND bn.bug_id = b.id
689 $t_project_where $t_from_where $t_to_where
690 GROUP BY bn.bug_id, u.id, u.username, b.summary
693 $result = db_query( $query );
695 $t_cost_min = $c_cost / 60;
697 while( $row = db_fetch_array( $result ) ) {
698 $t_total_cost = $t_cost_min * $row['sum_time_tracking'];
699 $row['cost'] = $t_total_cost;
707 * Clear a bugnote from the cache or all bug notes if no bugnote id specified.
708 * @param int bugnote id to clear (optional)
712 function bugnote_clear_cache( $p_bugnote_id = null ) {
713 global $g_cache_bugnote, $g_cache_bugnotes;
715 if( null === $p_bugnote_id ) {
716 $g_cache_bugnote = array();
718 unset( $g_cache_bugnote[(int) $p_bugnote_id] );
720 $g_cache_bugnotes = array();