]> git.pld-linux.org Git - packages/eventum.git/blobdiff - eventum-order.patch
Up to 3.10.12
[packages/eventum.git] / eventum-order.patch
index 0594dabf28342fb7295a4b01cca7007ae5839641..5764514ba18bbecb6b4ef71b611d190aebb24fb3 100644 (file)
@@ -1,10 +1,8 @@
 --- eventum-2.2/htdocs/ajax/order.php  1970-01-01 02:00:00.000000000 +0200
 +++ eventum-2.2-order/htdocs/ajax/order.php    2009-10-12 22:10:36.429185594 +0300
-@@ -0,0 +1,72 @@
+@@ -0,0 +1,69 @@
 +<?
-+require_once(dirname(__FILE__) . '/../init.php');
-+require_once(APP_INC_PATH . "class.auth.php");
-+require_once(APP_INC_PATH . "class.issue.php");
++require_once dirname(__FILE__) . '/../../init.php';
 +
 +// check login
 +if (!Auth::hasValidCookie(APP_COOKIE)) {
@@ -12,7 +10,6 @@
 +}
 +
 +
-+
 +if (!isset($_POST['before']) || !isset($_POST['after'])) {
 +      exit;
 +}
 +        }
 +    }
 +}
---- eventum-2.2/htdocs/ajax/update.php 1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/htdocs/ajax/update.php   2009-10-12 22:10:36.439185157 +0300
-@@ -0,0 +1,38 @@
-+<?
-+require_once(dirname(__FILE__) . '/../init.php');
-+require_once(APP_INC_PATH . "class.auth.php");
-+require_once(APP_INC_PATH . "class.issue.php");
-+
-+// check login
-+if (!Auth::hasValidCookie(APP_COOKIE)) {
-+    exit;
-+}
-+
-+if (!is_numeric($_POST['issueID'])) {
-+    exit;
-+}
-+
-+switch ($_POST['fieldName']) {
-+  case 'expected_resolution_date':
-+    $day = (int)$_POST['day'];
-+    $month = (int)$_POST['month'];
-+      $year = (int)$_POST['year'];
-+      if ($day == 0 && $month == 1 && $year == 0) {
-+              // clear button
-+              $date = null;
-+      } else {
-+              $date = sprintf('%04d-%02d-%02d', $year, $month, $day);
-+      }
-+      if (Issue::updateField($_POST['issueID'], $_POST['fieldName'], $date) !== -1) {
-+              if (!is_null($date)) {
-+            echo Date_Helper::getSimpleDate(sprintf('%04d-%02d-%02d', $year, $month, $day), false);
-+              }
-+    } else {
-+        echo 'update failed';
-+    }
-+    exit;
-+  break;
-+  default:
-+      die('object type not supported');
-+  break;
-+}
---- eventum-2.2/htdocs/css/style.css   2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/htdocs/css/style.css     2009-10-12 22:10:36.439185157 +0300
-@@ -172,4 +172,22 @@
-     font-size: 70%;
-     font-family: Verdana, Arial, Helvetica, sans-serif;
-     padding: 10px;
--}
-\ No newline at end of file
-+}
+--- eventum-2.3.1/htdocs/css/style.css~        2011-09-15 09:36:55.000000000 +0300
++++ eventum-2.3.1/htdocs/css/style.css 2011-09-15 09:38:23.668223576 +0300
+@@ -177,6 +177,24 @@
+     cursor: pointer;
+ }
 +.tDnD_whileDrag td {
 +    background-color: #ffffdd;
 +}
 +    background-repeat: no-repeat;
 +    background-position: center center;
 +}
---- eventum-2.2/htdocs/js/global.js    2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/htdocs/js/global.js      2009-10-12 22:10:36.439185157 +0300
-@@ -798,4 +798,44 @@
-         firstDay: user_prefs.week_firstday
-     });
- });
+ ul.excerpts {
+     list-style: none;
+@@ -187,4 +205,4 @@
+ ul.excerpts ul {
+     list-style-type: none;
+     padding-left: 1em;
+-}
+\ No newline at end of file
++}
+--- eventum-2.2/htdocs/js/jquery/jquery.tablednd.js    1970-01-01 02:00:00.000000000 +0200
++++ eventum-2.2-order/htdocs/js/jquery/jquery.tablednd.js      2009-10-12 22:10:36.435851675 +0300
+@@ -0,0 +1,382 @@
++/**
++ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
++ * You can set up various options to control how the system will work
++ * Copyright (c) Denis Howlett <denish@isocra.com>
++ * Licensed like jQuery, see http://docs.jquery.com/License.
++ *
++ * Configuration options:
++ * 
++ * onDragStyle
++ *     This is the style that is assigned to the row during drag. There are limitations to the styles that can be
++ *     associated with a row (such as you can't assign a border--well you can, but it won't be
++ *     displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
++ *     a map (as used in the jQuery css(...) function).
++ * onDropStyle
++ *     This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
++ *     to what you can do. Also this replaces the original style, so again consider using onDragClass which
++ *     is simply added and then removed on drop.
++ * onDragClass
++ *     This class is added for the duration of the drag and then removed when the row is dropped. It is more
++ *     flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
++ *     is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
++ *     stylesheet.
++ * onDrop
++ *     Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
++ *     and the row that was dropped. You can work out the new order of the rows by using
++ *     table.rows.
++ * onDragStart
++ *     Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
++ *     table and the row which the user has started to drag.
++ * onAllowDrop
++ *     Pass a function that will be called as a row is over another row. If the function returns true, allow 
++ *     dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
++ *     the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
++ * scrollAmount
++ *     This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
++ *     window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
++ *     FF3 beta
++ * dragHandle
++ *     This is the name of a class that you assign to one or more cells in each row that is draggable. If you
++ *     specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
++ *     will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
++ *     the whole row is draggable.
++ * 
++ * Other ways to control behaviour:
++ *
++ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
++ * that you don't want to be draggable.
++ *
++ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
++ * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
++ * an ID as must all the rows.
++ *
++ * Other methods:
++ *
++ * $("...").tableDnDUpdate() 
++ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
++ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
++ * The table maintains the original configuration (so you don't have to specify it again).
++ *
++ * $("...").tableDnDSerialize()
++ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
++ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
++ *
++ * Known problems:
++ * - Auto-scoll has some problems with IE7  (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
++ * 
++ * Version 0.2: 2008-02-20 First public version
++ * Version 0.3: 2008-02-07 Added onDragStart option
++ *                         Made the scroll amount configurable (default is 5 as before)
++ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
++ *                         Added onAllowDrop to control dropping
++ *                         Fixed a bug which meant that you couldn't set the scroll amount in both directions
++ *                         Added serialize method
++ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
++ *                         draggable
++ *                         Improved the serialize method to use a default (and settable) regular expression.
++ *                         Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
++ */
++jQuery.tableDnD = {
++    /** Keep hold of the current table being dragged */
++    currentTable : null,
++    /** Keep hold of the current drag object if any */
++    dragObject: null,
++    /** The current mouse offset */
++    mouseOffset: null,
++    /** Remember the old value of Y so that we don't do too much processing */
++    oldY: 0,
 +
-+$(document).ready(function() {
-+    // dialog type calender isn't working in Konqueror beacuse it's not a supported browser for either jQuery or jQuery UI
-+    // http://groups.google.com/group/jquery-ui/browse_thread/thread/ea61238c34cb5f33/046837b02fb90b5c
-+    if (navigator.appName != 'Konqueror') {
-+        $(".inline_date_pick").click(function() {
-+        var masterObj = this;
-+        var masterObjPos = $(masterObj).offset();
-+        // offset gives uses top and left but datepicker needs pageX and pageY
-+        var masterObjPos = {pageX: masterObjPos.left, pageY: masterObjPos.top};
-+        $(this).datepicker(
-+            // we use dialog type calender so we won't haveto have a hidden element on the page
-+            'dialog',
-+            // selected date
-+            masterObj.innerHTML,
-+            // onclick handler
-+            function (date, dteObj) {
-+                fieldName = masterObj.id.substr(0,masterObj.id.indexOf('|'));
-+                issueID = masterObj.id.substr(masterObj.id.indexOf('|')+1);
-+                              if (date == '') {
-+                                      // clear button
-+                                      dteObj.selectedDay = 0;
-+                                      dteObj.selectedMonth = 0;
-+                                      dteObj.selectedYear = 0;
-+                              }
-+                              //alertProperties(date);
-+                $.post("/ajax/update.php", {fieldName: fieldName, issueID: issueID, day: dteObj.selectedDay, month: (dteObj.selectedMonth+1), year: dteObj.selectedYear}, function(data) {
-+                    masterObj.innerHTML = data;
-+                }, "text");
-+            },
-+            // config
-+            {dateFormat: 'dd M yy', duration: ""},
-+            // position of the datepicker calender - taken from div's offset
-+            masterObjPos
-+        );
-+        return false;
++    /** Actually build the structure */
++    build: function(options) {
++        // Set up the defaults if any
++
++        this.each(function() {
++            // This is bound to each matching table, set up the defaults and override with user options
++            this.tableDnDConfig = jQuery.extend({
++                onDragStyle: null,
++                onDropStyle: null,
++                              // Add in the default class for whileDragging
++                              onDragClass: "tDnD_whileDrag",
++                onDrop: null,
++                onDragStart: null,
++                scrollAmount: 5,
++                              serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
++                              serializeParamName: null, // If you want to specify another parameter name instead of the table ID
++                dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
++            }, options || {});
++            // Now make the rows draggable
++            jQuery.tableDnD.makeDraggable(this);
 +        });
-+    }
-+});
 +
- //-->
---- eventum-2.2/htdocs/js/global.js.~1~        1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/htdocs/js/global.js.~1~  2009-09-14 18:07:55.000000000 +0300
-@@ -0,0 +1,801 @@
-+<!--
-+// @(#) $Id$
-+var today = new Date();
-+var expires = new Date(today.getTime() + (56 * 86400000));
++        // Now we need to capture the mouse up and mouse move event
++        // We can use bind so that we don't interfere with other event handlers
++        jQuery(document)
++            .bind('mousemove', jQuery.tableDnD.mousemove)
++            .bind('mouseup', jQuery.tableDnD.mouseup);
 +
-+function addFileRow(element_name, field_name)
-+{
-+    if (document.all) {
-+        var fileTable = document.all[element_name];
-+    } else if (!document.all && document.getElementById) {
-+        var fileTable = document.getElementById(element_name);
-+    }
-+    if (!fileTable) {
-+        return;
-+    }
-+    rows = fileTable.rows.length;
++        // Don't break the chain
++        return this;
++    },
 +
-+    // check if last box is empty and if it is, don't add another
-+    if (document.all) {
-+        var last_field = document.all[field_name + '_' + rows];
-+    } else if (!document.all && document.getElementById) {
-+        var last_field = document.getElementById(field_name + '_' + rows);
-+    }
-+    if (last_field && last_field.value == '') {
-+        return;
-+    }
++    /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
++    makeDraggable: function(table) {
++        var config = table.tableDnDConfig;
++              if (table.tableDnDConfig.dragHandle) {
++                      // We only need to add the event to the specified cells
++                      var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
++                      cells.each(function() {
++                              // The cell is bound to "this"
++                jQuery(this).mousedown(function(ev) {
++                    jQuery.tableDnD.dragObject = this.parentNode;
++                    jQuery.tableDnD.currentTable = table;
++                    jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
++                    if (config.onDragStart) {
++                        // Call the onDrop method if there is one
++                        config.onDragStart(table, this);
++                    }
++                    return false;
++                });
++                      })
++              } else {
++                      // For backwards compatibility, we add the event to the whole row
++              var rows = jQuery("tr", table); // get all the rows as a wrapped set
++              rows.each(function() {
++                              // Iterate through each row, the row is bound to "this"
++                              var row = jQuery(this);
++                              if (! row.hasClass("nodrag")) {
++                      row.mousedown(function(ev) {
++                          if (ev.target.tagName == "TD") {
++                              jQuery.tableDnD.dragObject = this;
++                              jQuery.tableDnD.currentTable = table;
++                              jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
++                              if (config.onDragStart) {
++                                  // Call the onDrop method if there is one
++                                  config.onDragStart(table, this);
++                              }
++                              return false;
++                          }
++                      }).css("cursor", "move"); // Store the tableDnD object
++                              }
++                      });
++              }
++      },
 +
-+    newRow = fileTable.insertRow(rows);
-+    cell = newRow.insertCell(0);
-+    if (document.all) {
-+        cell.innerHTML = '<input id="' + field_name + '_' + (rows+1) + '" class="shortcut" size="40" type="file" name="' + field_name + '" onChange="javascript:addFileRow(\'' + element_name + '\', \'' + field_name + '\');">';
-+    } else {
-+        var input = document.createElement('INPUT');
-+        input.setAttribute('type', 'file');
-+        input.name = field_name;
-+        input.className = 'shortcut';
-+        input.size = 40;
-+        input.onchange = new Function('addFileRow(\'' + element_name + '\', \'' + field_name + '\');');
-+        input.id = field_name + '_' + (rows+1);
-+        cell.appendChild(input);
-+    }
-+}
++      updateTables: function() {
++              this.each(function() {
++                      // this is now bound to each matching table
++                      if (this.tableDnDConfig) {
++                              jQuery.tableDnD.makeDraggable(this);
++                      }
++              })
++      },
 +
-+function inArray(value, stack)
-+{
-+    for (var i = 0; i < stack.length; i++) {
-+        if (stack[i] == value) {
-+            return true;
++    /** Get the mouse coordinates from the event (allowing for browser differences) */
++    mouseCoords: function(ev){
++        if(ev.pageX || ev.pageY){
++            return {x:ev.pageX, y:ev.pageY};
 +        }
-+    }
-+    return false;
-+}
++        return {
++            x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
++            y:ev.clientY + document.body.scrollTop  - document.body.clientTop
++        };
++    },
 +
-+function getEmailFromAddress(str)
-+{
-+    var first_pos = str.lastIndexOf('<');
-+    var second_pos = str.lastIndexOf('>');
-+    if ((first_pos != -1) && (second_pos != -1)) {
-+        return str.substring(first_pos+1, second_pos);
-+    } else {
-+        return str;
-+    }
-+}
++    /** Given a target element and a mouse event, get the mouse offset from that element.
++        To do this we need the element's position and the mouse position */
++    getMouseOffset: function(target, ev) {
++        ev = ev || window.event;
 +
-+function closeAndRefresh()
-+{
-+    opener.location.href = opener.location;
-+    window.close();
-+}
++        var docPos    = this.getPosition(target);
++        var mousePos  = this.mouseCoords(ev);
++        return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
++    },
 +
-+function str_replace(s, srch, rplc)
-+{
-+    var tmp = s;
-+    var tmp_before = new String();
-+    var tmp_after = new String();
-+    var tmp_output = new String();
-+    var int_before = 0;
-+    var int_after = 0;
++    /** Get the position of an element by going up the DOM tree and adding up all the offsets */
++    getPosition: function(e){
++        var left = 0;
++        var top  = 0;
++        /** Safari fix -- thanks to Luis Chato for this! */
++        if (e.offsetHeight == 0) {
++            /** Safari 2 doesn't correctly grab the offsetTop of a table row
++            this is detailed here:
++            http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
++            the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
++            note that firefox will return a text node as a first child, so designing a more thorough
++            solution may need to take that into account, for now this seems to work in firefox, safari, ie */
++            e = e.firstChild; // a table cell
++        }
 +
-+    while (tmp.toUpperCase().indexOf(srch.toUpperCase()) > -1) {
-+        int_before = tmp.toUpperCase().indexOf(srch.toUpperCase());
-+        tmp_before = tmp.substring(0, int_before);
-+        tmp_output = tmp_output + tmp_before;
-+        tmp_output = tmp_output + rplc;
-+        int_after = tmp.length - srch.length + 1;
-+        tmp = tmp.substring(int_before + srch.length);
-+    }
++        while (e.offsetParent){
++            left += e.offsetLeft;
++            top  += e.offsetTop;
++            e     = e.offsetParent;
++        }
 +
-+    return tmp_output + tmp;
-+}
-+
-+function displayFixedWidth(element_name)
-+{
-+    var el = getPageElement(element_name);
-+    // only do this for mozilla
-+    if (is_nav6up) {
-+        var content = el.innerHTML;
-+        el.innerHTML = '<pre>' + str_replace(content, "<br>", '') + '</pre>';
-+        el.className = '';
-+    }
-+    el.style.fontFamily = 'Mono, Monaco, Courier New, Courier';
-+    el.style.whiteSpace = 'pre';
-+}
-+
-+function showSelections(form_name, field_name)
-+{
-+    var f = getForm(form_name);
-+    var field = getFormElement(f, field_name);
-+    var selections = getSelectedItems(field);
-+    var selected_names = new Array();
-+    for (var i = 0; i < selections.length; i++) {
-+        selected_names.push(selections[i].text);
-+    }
-+    var display_div = getPageElement('selection_' + field_name);
-+    display_div.innerHTML = 'Current Selections: ' + selected_names.join(', ');
-+}
-+
-+function replaceWords(str, original, replacement)
-+{
-+    var lines = str.split("\n");
-+    for (var i = 0; i < lines.length; i++) {
-+        lines[i] = replaceWordsOnLine(lines[i], original, replacement);
-+    }
-+    return lines.join("\n");
-+}
-+
-+function replaceWordsOnLine(str, original, replacement)
-+{
-+    var words = str.split(' ');
-+    for (var i = 0; i < words.length; i++) {
-+        words[i] = words[i].replace(/^\s*/, '').replace(/\s*$/, '');
-+        if (words[i] == original) {
-+            words[i] = replacement;
-+        }
-+    }
-+    return words.join(' ');
-+}
-+
-+function checkSpelling(form_name, field_name)
-+{
-+    var features = 'width=420,height=400,top=30,left=30,resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
-+    var popupWin = window.open('spell_check.php?form_name=' + form_name + '&field_name=' + field_name, '_spellChecking', features);
-+    popupWin.focus();
-+}
-+
-+function updateTimeFields(form_name, year_field, month_field, day_field, hour_field, minute_field, date)
-+{
-+    var f = getForm(form_name);
-+    if (typeof date == 'undefined') {
-+        date = new Date();
-+    }
-+    selectOption(f, month_field, padDateValue(date.getMonth()+1));
-+    selectOption(f, day_field, padDateValue(date.getDate()));
-+    selectOption(f, year_field, date.getFullYear());
-+    selectOption(f, hour_field, padDateValue(date.getHours()));
-+    // minutes need special case due the 5 minute granularity
-+    var minutes = Math.floor(date.getMinutes() / 5) * 5;
-+    selectOption(f, minute_field, padDateValue(minutes));
-+}
-+
-+function padDateValue(str)
-+{
-+    if (str.length == 1) {
-+        str = '0' + str;
-+    }
-+    return str;
-+}
-+
-+function resizeTextarea(page_name, form_name, field_name, change)
-+{
-+    var f = getForm(form_name);
-+    var field = getFormElement(f, field_name);
-+    field.cols = field.cols + change;
-+    var cookie_name = 'textarea_' + page_name + '_' + field_name;
-+    setCookie(cookie_name, field.cols, expires);
-+}
-+
-+function removeOptionByValue(f, field_name, value)
-+{
-+    var field = getFormElement(f, field_name);
-+    for (var i = 0; i < field.options.length; i++) {
-+        if (field.options[i].value == value) {
-+            field.options[i] = null;
-+        }
-+    }
-+}
++        left += e.offsetLeft;
++        top  += e.offsetTop;
 +
-+function getTotalCheckboxes(f, field_name)
-+{
-+    var total = 0;
-+    for (var i = 0; i < f.elements.length; i++) {
-+        if (f.elements[i].name == field_name) {
-+            total++;
-+        }
-+    }
-+    return total;
-+}
++        return {x:left, y:top};
++    },
 +
-+function getTotalCheckboxesChecked(f, field_name)
-+{
-+    var total = 0;
-+    for (var i = 0; i < f.elements.length; i++) {
-+        if ((f.elements[i].name == field_name) && (f.elements[i].checked)) {
-+            total++;
++    mousemove: function(ev) {
++        if (jQuery.tableDnD.dragObject == null) {
++            return;
 +        }
-+    }
-+    return total;
-+}
 +
-+function hideComboBoxes(except_field)
-+{
-+    for (var i = 0; i < document.forms.length; i++) {
-+        for (var y = 0; y < document.forms[i].elements.length; y++) {
-+            if (((document.forms[i].elements[y].type == 'select-one') ||
-+            (document.forms[i].elements[y].type == 'select-multiple')) &&
-+            (document.forms[i].elements[y].name != except_field) &&
-+            (document.forms[i].elements[y].name != 'lookup') &&
-+            (document.forms[i].elements[y].name != 'lookup[]')) {
-+                document.forms[i].elements[y].style.visibility = 'hidden';
-+            }
-+        }
-+    }
-+}
++        var dragObj = jQuery(jQuery.tableDnD.dragObject);
++        var config = jQuery.tableDnD.currentTable.tableDnDConfig;
++        var mousePos = jQuery.tableDnD.mouseCoords(ev);
++        var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
++        //auto scroll the window
++          var yOffset = window.pageYOffset;
++              if (document.all) {
++              // Windows version
++              //yOffset=document.body.scrollTop;
++              if (typeof document.compatMode != 'undefined' &&
++                   document.compatMode != 'BackCompat') {
++                 yOffset = document.documentElement.scrollTop;
++              }
++              else if (typeof document.body != 'undefined') {
++                 yOffset=document.body.scrollTop;
++              }
 +
-+function showComboBoxes()
-+{
-+    for (var i = 0; i < document.forms.length; i++) {
-+        for (var y = 0; y < document.forms[i].elements.length; y++) {
-+            if (((document.forms[i].elements[y].type == 'select-one') ||
-+            (document.forms[i].elements[y].type == 'select-multiple')) &&
-+            (document.forms[i].elements[y].name != 'lookup') &&
-+            (document.forms[i].elements[y].name != 'lookup[]')) {
-+                document.forms[i].elements[y].style.visibility = 'visible';
++          }
++                  
++              if (mousePos.y-yOffset < config.scrollAmount) {
++              window.scrollBy(0, -config.scrollAmount);
++          } else {
++            var windowHeight = window.innerHeight ? window.innerHeight
++                    : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
++            if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
++                window.scrollBy(0, config.scrollAmount);
 +            }
 +        }
-+    }
-+}
-+
-+function getOverlibContents(options, target_form, target_field, is_multiple)
-+{
-+    hideComboBoxes(target_field);
-+    var html = '<form name="overlib_form" onSubmit="javascript:return lookupOption(this, \'' + target_form + '\', \'' + target_field + '\');">' + options + '<br /><input name="search" class="lookup_field_overlib" type="text" size="24" value="paste or start typing here" onBlur="javascript:this.value=\'paste or start typing here\';" onFocus="javascript:this.value=\'\';" onKeyUp="javascript:lookupField(this.form, this, \'lookup';
-+    if ((is_multiple != null) && (is_multiple == true)) {
-+        html += '[]';
-+    }
-+    html += '\');"><input class="button_overlib" type="submit" value="Lookup"><br />'
-+    + '<input type="text" name="id_number" size="24" class="lookup_field_overlib" value="id #" onFocus="javascript:this.value=\'\';">'
-+    + '<input type="button" class="button_overlib" value="Add By ID" onClick="lookupByID(document.forms[\'overlib_form\'].id_number, \'' + target_form + '\', \'' + target_field + '\')"></form>';
-+    return html;
-+}
 +
-+function getFillInput(options, target_form, target_field)
-+{
-+    hideComboBoxes(target_field);
-+    return '<form onSubmit="javascript:return fillInput(this, \'' + target_form + '\', \'' + target_field + '\');">' + options + '<input class="button_overlib" type="submit" value="Lookup"><br><input name="search" class="lookup_field_overlib" type="text" size="24" value="paste or start typing here" onBlur="javascript:this.value=\'paste or start typing here\';" onFocus="javascript:this.value=\'\';" onKeyUp="javascript:lookupField(this.form, this, \'lookup\');"></form>';
-+}
 +
-+function lookupOption(f, target_form, target_field)
-+{
-+    var w = document;
-+    for (var i = 0; i < w.forms.length; i++) {
-+        if (w.forms[i].name == target_form) {
-+            var test = getFormElement(f, 'lookup');
-+            if (!test) {
-+                var field = getFormElement(f, 'lookup[]');
-+                var target = getFormElement(getForm(target_form), target_field);
-+                clearSelectedOptions(target);
-+                selectOptions(w.forms[i], target_field, getSelectedItems(field));
-+            } else {
-+                options = getSelectedOption(f, 'lookup');
-+                if (options == -1) {
-+                    return false;
++        if (y != jQuery.tableDnD.oldY) {
++            // work out if we're going up or down...
++            var movingDown = y > jQuery.tableDnD.oldY;
++            // update the old value
++            jQuery.tableDnD.oldY = y;
++            // update the style to show we're dragging
++                      if (config.onDragClass) {
++                              dragObj.addClass(config.onDragClass);
++                      } else {
++                  dragObj.css(config.onDragStyle);
++                      }
++            // If we're over a row then move the dragged row to there so that the user sees the
++            // effect dynamically
++            var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
++            if (currentRow) {
++                // TODO worry about what happens when there are multiple TBODIES
++                if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
++                    jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
++                } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
++                    jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
 +                }
-+                selectOption(w.forms[i], target_field, options);
-+            }
-+            nd();
-+            showComboBoxes();
-+            break;
-+        }
-+    }
-+    return false;
-+}
-+
-+function lookupByID(field, target_form, target_field)
-+{
-+    if (!isNumberOnly(field.value)) {
-+        alert('Please enter numbers only');
-+    } else {
-+        // try to find value in targer field.
-+        target_obj = document.forms[target_form].elements[target_field];
-+        found = false;
-+        for (i = 0;i<target_obj.options.length; i++) {
-+            if (target_obj.options[i].value == field.value) {
-+                found = true;
-+                target_obj.options[i].selected = true;
-+            }
-+        }
-+        if (found == false) {
-+            alert('ID #' + field.value + ' was not found');
-+        } else {
-+            field.value = '';
-+            // check if we should call "showSelection"
-+            if (document.getElementById('selection_' + target_field) != null) {
-+                showSelections(target_form, target_field)
 +            }
 +        }
-+    }
-+    return false;
-+}
-+
-+function fillInput(f, target_form, target_field)
-+{
-+    var exists = getFormElement(f, 'lookup');
-+    var target_f = getForm(target_form);
-+    if (!exists) {
-+        var field = getFormElement(f, 'lookup[]');
-+        var target_field = getFormElement(target_f, target_field);
-+        target_field.value = '';
-+        var values = getValues(getSelectedItems(field));
-+        target_field.value = values.join('; ');
-+        errorDetails(target_f, target_field, false);
-+    } else {
-+        var field_value = getSelectedOption(f, 'lookup');
-+        var field = getFormElement(target_f, target_field);
-+        field.value = field_value;
-+        errorDetails(target_f, target_field, false);
-+    }
-+    nd();
-+    showComboBoxes();
-+    return false;
-+}
 +
-+function lookupField(f, search_field, field_name, callbacks)
-+{
-+    var search = search_field.value;
-+    if (isWhitespace(search)) {
 +        return false;
-+    }
-+    var target_field = getFormElement(f, field_name);
-+    if (!target_field) {
-+        target_field = getFormElement(f, field_name + '[]');
-+    }
-+    for (var i = 0; i < target_field.options.length; i++) {
-+        var value = target_field.options[i].text.toUpperCase();
-+        if (target_field.type == 'select-multiple') {
-+            // if we are targetting a multiple select box, then unselect everything
-+            // before selecting the matched option
-+            if (startsWith(value, search.toUpperCase())) {
-+                clearSelectedOptions(target_field);
-+                target_field.options[i].selected = true;
-+                // handle calling any callbacks
-+                if (callbacks != null) {
-+                    for (var y = 0; y < callbacks.length; y++) {
-+                        eval(callbacks[y] + ';');
-+                    }
-+                }
-+                return true;
++    },
++
++    /** We're only worried about the y position really, because we can only move rows up and down */
++    findDropTargetRow: function(draggedRow, y) {
++        var rows = jQuery.tableDnD.currentTable.rows;
++        for (var i=0; i<rows.length; i++) {
++            var row = rows[i];
++            var rowY    = this.getPosition(row).y;
++            var rowHeight = parseInt(row.offsetHeight)/2;
++            if (row.offsetHeight == 0) {
++                rowY = this.getPosition(row.firstChild).y;
++                rowHeight = parseInt(row.firstChild.offsetHeight)/2;
 +            }
-+        } else {
-+            // normal drop-down boxes will search across the option value, and
-+            // not just the beginning of it (e.g. '*hello*' instead of 'hello*')
-+            if (value.indexOf(search.toUpperCase()) != -1) {
-+                target_field.options[i].selected = true;
-+                // handle calling any callbacks
-+                if (callbacks != null) {
-+                    for (var y = 0; y < callbacks.length; y++) {
-+                        eval(callbacks[y] + ';');
++            // Because we always have to insert before, we need to offset the height a bit
++            if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
++                // that's the row we're over
++                              // If it's the same as the current row, ignore it
++                              if (row == draggedRow) {return null;}
++                var config = jQuery.tableDnD.currentTable.tableDnDConfig;
++                if (config.onAllowDrop) {
++                    if (config.onAllowDrop(draggedRow, row)) {
++                        return row;
++                    } else {
++                        return null;
++                    }
++                } else {
++                                      // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
++                    var nodrop = jQuery(row).hasClass("nodrop");
++                    if (! nodrop) {
++                        return row;
++                    } else {
++                        return null;
 +                    }
 +                }
-+                return true;
++                return row;
 +            }
 +        }
-+    }
-+    target_field.selectedIndex = 0;
-+}
-+
-+function clearSelectedOptions(field)
-+{
-+    for (var i = 0; i < field.options.length; i++) {
-+        field.options[i].selected = false;
-+    }
-+}
-+
-+function selectAllOptions(f, field_name)
-+{
-+    var field = getFormElement(f, field_name);
-+    for (var y = 0; y < field.options.length; y++) {
-+        field.options[y].selected = true;
-+    }
-+}
++        return null;
++    },
 +
-+function selectOptions(f, field_name, values)
-+{
-+    var field = getFormElement(f, field_name);
-+    for (var i = 0; i < values.length; i++) {
-+        for (var y = 0; y < field.options.length; y++) {
-+            if (field.options[y].value == values[i].value) {
-+                field.options[y].selected = true;
++    mouseup: function(e) {
++        if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
++            var droppedRow = jQuery.tableDnD.dragObject;
++            var config = jQuery.tableDnD.currentTable.tableDnDConfig;
++            // If we have a dragObject, then we need to release it,
++            // The row will already have been moved to the right place so we just reset stuff
++                      if (config.onDragClass) {
++                  jQuery(droppedRow).removeClass(config.onDragClass);
++                      } else {
++                  jQuery(droppedRow).css(config.onDropStyle);
++                      }
++            jQuery.tableDnD.dragObject   = null;
++            if (config.onDrop) {
++                // Call the onDrop method if there is one
++                config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
 +            }
++            jQuery.tableDnD.currentTable = null; // let go of the table too
 +        }
-+    }
-+}
-+
-+function selectOption(f, field_name, value)
-+{
-+    field = getFormElement(f, field_name);
-+    for (var i = 0; i < field.options.length; i++) {
-+        if (field.options[i].value == value) {
-+            field.options[i].selected = true;
-+            return true;
-+        }
-+    }
-+}
-+
-+function setHiddenFieldValue(f, field_name, value)
-+{
-+    var field = getFormElement(f, field_name);
-+    field.value = value;
-+}
-+
-+function getForm(form_name)
-+{
-+    for (var i = 0; i < document.forms.length; i++) {
-+        if (document.forms[i].name == form_name) {
-+            return document.forms[i];
-+        }
-+    }
-+}
-+
-+function getPageElement(id)
-+{
-+    if (document.getElementById) {
-+        return document.getElementById(id);
-+    } else if (document.all) {
-+        return document.all[id];
-+    }
-+}
-+
-+function getOpenerPageElement(name)
-+{
-+    if (window.opener.document.getElementById) {
-+        return window.opener.document.getElementById(name);
-+    } else if (window.opener.document.all) {
-+        return window.opener.document.all[name];
-+    }
-+}
++    },
 +
-+function getFormElement(f, field_name, num)
-+{
-+    var elements = document.getElementsByName(field_name);
-+    var y = 0;
-+    for (var i = 0; i < elements.length; i++) {
-+        if (f != elements[i].form) {
-+            continue;
-+        }
-+        if (num != null) {
-+            if (y == num) {
-+                return elements[i];
-+            }
-+            y++;
++    serialize: function() {
++        if (jQuery.tableDnD.currentTable) {
++            return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
 +        } else {
-+            return elements[i];
++            return "Error: No Table id set, you need to set an id on your table and every row";
 +        }
-+    }
-+    return false;
-+}
++    },
 +
-+function getSelectedItems(field)
-+{
-+    var selected = new Array();
-+    for (var i = 0; i < field.options.length; i++) {
-+        if (field.options[i].selected) {
-+            selected[selected.length] = field.options[i];
-+        }
-+    }
-+    return selected;
-+}
++      serializeTable: function(table) {
++        var result = "";
++        var tableId = table.id;
++        var rows = table.rows;
++        for (var i=0; i<rows.length; i++) {
++            if (result.length > 0) result += "&";
++            var rowId = rows[i].id;
++            if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
++                rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
++            }
 +
-+function getSelectedOptionValues(f, field_name)
-+{
-+    var field = getFormElement(f, field_name);
-+    var selected = new Array();
-+    for (var i = 0; i < field.options.length; i++) {
-+        if (field.options[i].selected) {
-+            selected[selected.length] = field.options[i].value;
-+        }
-+    }
-+    return selected;
-+}
-+
-+function removeAllOptions(f, field_name)
-+{
-+    var field = getFormElement(f, field_name);
-+    if (field.options.length > 0) {
-+        field.options[0] = null;
-+        removeAllOptions(f, field_name);
-+    }
-+}
-+
-+function getValues(list)
-+{
-+    var values = new Array();
-+    for (var i = 0; i < list.length; i++) {
-+        values[values.length] = list[i].value;
-+    }
-+    return values;
-+}
-+
-+function optionExists(field, option)
-+{
-+    for (var i = 0; i < field.options.length; i++) {
-+        if (field.options[i].text == option.text) {
-+            return true;
-+        }
-+    }
-+    return false;
-+}
-+
-+function addOptions(f, field_name, options)
-+{
-+    var field = getFormElement(f, field_name);
-+    for (var i = 0; i < options.length; i++) {
-+        if (!optionExists(field, options[i])) {
-+            field.options.length = field.options.length + 1;
-+            field.options[field.options.length-1].text = options[i].text;
-+            field.options[field.options.length-1].value = options[i].value;
-+        }
-+    }
-+}
-+
-+function replaceParam(str, param, new_value)
-+{
-+    if (str.indexOf("?") == -1) {
-+        return param + "=" + new_value;
-+    } else {
-+        var pieces = str.split("?");
-+        var params = pieces[1].split("&");
-+        var new_params = new Array();
-+        for (var i = 0; i < params.length; i++) {
-+            if (params[i].indexOf(param + "=") == 0) {
-+                params[i] = param + "=" + new_value;
-+            }
-+            new_params[i] = params[i];
-+        }
-+        // check if the parameter doesn't exist on the URL
-+        if ((str.indexOf("?" + param + "=") == -1) && (str.indexOf("&" + param + "=") == -1)) {
-+            new_params[new_params.length] = param + "=" + new_value;
-+        }
-+        return new_params.join("&");
-+    }
-+}
-+
-+function checkRadio(form_name, field_name, num)
-+{
-+    var f = getForm(form_name);
-+    var field = getFormElement(f, field_name, num);
-+    if (!field.disabled) {
-+        field.checked = true;
-+    }
-+}
-+
-+function toggleCheckbox(form_name, field_name, num)
-+{
-+    var f = getForm(form_name);
-+    var checkbox = getFormElement(f, field_name, num);
-+    if (checkbox.disabled) {
-+        return false;
-+    }
-+    if (checkbox.checked) {
-+        checkbox.checked = false;
-+    } else {
-+        checkbox.checked = true;
-+    }
-+}
-+
-+var toggle = 'off';
-+function toggleSelectAll(f, field_name)
-+{
-+    for (var i = 0; i < f.elements.length; i++) {
-+        if (f.elements[i].disabled) {
-+            continue;
-+        }
-+        if (f.elements[i].name == field_name) {
-+            if (toggle == 'off') {
-+                f.elements[i].checked = true;
-+            } else {
-+                f.elements[i].checked = false;
-+            }
-+        }
-+    }
-+    if (toggle == 'off') {
-+        toggle = 'on';
-+    } else {
-+        toggle = 'off';
-+    }
-+}
-+
-+function getCookies()
-+{
-+    var t = new Array();
-+    var pieces = new Array();
-+    var cookies = new Object();
-+    if (document.cookie) {
-+        t = document.cookie.split(';');
-+        for (var i = 0; i < t.length; i++) {
-+            pieces = t[i].split('=');
-+            eval('cookies.' + pieces[0].replace('[', '_').replace(']', '_') + ' = "' + pieces[1] + '";');
-+        }
-+        return cookies;
-+    }
-+}
-+
-+function isElementVisible(element)
-+{
-+    if ((!element.style.display) || (element.style.display == getDisplayStyle())) {
-+        return true;
-+    } else {
-+        return false;
-+    }
-+}
-+
-+function toggleVisibility(title, create_cookie, use_inline)
-+{
-+    var element = getPageElement(title + '1');
-+    if (isElementVisible(element)) {
-+        var new_style = 'none';
-+    } else {
-+        var new_style = getDisplayStyle(use_inline);
-+    }
-+    var i = 1;
-+    while (1) {
-+        element = getPageElement(title + i);
-+        if (!element) {
-+            break;
-+        }
-+        element.style.display = new_style;
-+        i++;
-+    }
-+    // if any elements were found, then...
-+    if (i > 1) {
-+        var link_element = getPageElement(title + '_link');
-+        if (link_element) {
-+            if (new_style == 'none') {
-+                link_element.innerHTML = 'show';
-+                link_element.title = 'show details about this section';
-+            } else {
-+                link_element.innerHTML = 'hide';
-+                link_element.title = 'hide details about this section';
-+            }
-+        }
-+    }
-+    if (((create_cookie == null) || (create_cookie == false)) && (create_cookie != undefined)) {
-+        return false;
-+    } else {
-+        setCookie('visibility_' + title, new_style, expires);
-+    }
-+}
-+
-+function changeVisibility(title, visibility, use_inline)
-+{
-+    var element = getPageElement(title);
-+    if (visibility) {
-+        var new_style = getDisplayStyle(use_inline);
-+    } else {
-+        var new_style = 'none';
-+    }
-+    element.style.display = new_style;
-+}
-+
-+function getDisplayStyle(use_inline)
-+{
-+    // kind of hackish, but it works perfectly with IE6 and Mozilla 1.1
-+    if (is_ie5up) {
-+        if (use_inline == true) {
-+            return 'inline';
-+        } else {
-+            return 'block';
-+        }
-+    } else {
-+        return '';
-+    }
-+}
-+
-+function getCookie(name)
-+{
-+    var start = document.cookie.indexOf(name+"=");
-+    var len = start+name.length+1;
-+    if ((!start) && (name != document.cookie.substring(0,name.length))) return null;
-+    if (start == -1) return null;
-+    var end = document.cookie.indexOf(";",len);
-+    if (end == -1) end = document.cookie.length;
-+    return unescape(document.cookie.substring(len,end));
-+}
-+
-+function setCookie(name, value, expires, path, domain, secure)
-+{
-+    document.cookie = name + "=" +escape(value) +
-+    ( (expires) ? ";expires=" + expires.toGMTString() : "") +
-+    ( (path) ? ";path=" + path : "") +
-+    ( (domain) ? ";domain=" + domain : "") +
-+    ( (secure) ? ";secure" : "");
-+}
-+
-+function openHelp(rel_url, topic)
-+{
-+    var width = 500;
-+    var height = 450;
-+    var w_offset = 30;
-+    var h_offset = 30;
-+    var location = 'top=' + h_offset + ',left=' + w_offset + ',';
-+    if (screen.width) {
-+        location = 'top=' + h_offset + ',left=' + (screen.width - (width + w_offset)) + ',';
-+    }
-+    var features = 'width=' + width + ',height=' + height + ',' + location + 'resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
-+    var helpWin = window.open(rel_url + 'help.php?topic=' + topic, '_help', features);
-+    helpWin.focus();
-+}
-+
-+function selectOnlyValidOption(selectObj)
-+{
-+    if (selectObj.selectedIndex < 1) {
-+        if (selectObj.length == 1) {
-+            selectObj.selectedIndex = 0;
-+            return;
-+        }
-+        if (selectObj.length <= 2 && selectObj.options[0].value == -1) {
-+            selectObj.selectedIndex = 1;
-+            return;
-+        }
-+    }
-+}
-+
-+// this method will confirm that you want the window to close
-+var checkClose = false;
-+var closeConfirmMessage = 'Do you want to close this window?';
-+function handleClose()
-+{
-+    if (checkClose == true) {
-+        return closeConfirmMessage;
-+    } else {
-+        return;
-+    }
-+}
-+
-+function checkWindowClose(msg)
-+{
-+    if (msg == false) {
-+        checkClose = false;
-+    } else {
-+        checkClose = true;
-+        closeConfirmMessage = msg;
-+    }
-+}
-+
-+// Replace special characters MS uses for quotes with normal versions
-+function replaceSpecialCharacters(e)
-+{
-+    var s = new String(e.value);
-+    var newString = '';
-+    var thisChar;
-+    var charCode;
-+    for (i = 0; i < s.length; i++) {
-+        thisChar = s.charAt(i);
-+        charCode = s.charCodeAt(i);
-+        if ((charCode == 8220) || (charCode == 8221)) {
-+            thisChar = '"';
-+        } else if (charCode == 8217) {
-+            thisChar = "'";
-+        } else if (charCode == 8230) {
-+            thisChar = "...";
-+        } else if (charCode == 8226) {
-+            thisChar = "*";
-+        } else if (charCode == 8211) {
-+            thisChar = "-";
-+        }
-+        newString = newString + thisChar;
-+    }
-+    e.value = newString;
-+}
-+
-+
-+function getEventTarget(e)
-+{
-+    var targ;
-+    if (!e) var e = window.event;
-+    if (e.target) targ = e.target;
-+    else if (e.srcElement) targ = e.srcElement;
-+    if (targ.nodeType == 3) // defeat Safari bug
-+    targ = targ.parentNode;
-+
-+    return targ;
-+}
-+
-+// call when document ready
-+$(document).ready(function() {
-+    $('.date_picker').datepicker({
-+        dateFormat: 'yy-mm-dd',
-+        firstDay: user_prefs.week_firstday
-+    });
-+});
-+//-->
---- eventum-2.2/htdocs/js/jquery/jquery.tablednd.js    1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/htdocs/js/jquery/jquery.tablednd.js      2009-10-12 22:10:36.435851675 +0300
-@@ -0,0 +1,382 @@
-+/**
-+ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
-+ * You can set up various options to control how the system will work
-+ * Copyright (c) Denis Howlett <denish@isocra.com>
-+ * Licensed like jQuery, see http://docs.jquery.com/License.
-+ *
-+ * Configuration options:
-+ * 
-+ * onDragStyle
-+ *     This is the style that is assigned to the row during drag. There are limitations to the styles that can be
-+ *     associated with a row (such as you can't assign a border--well you can, but it won't be
-+ *     displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
-+ *     a map (as used in the jQuery css(...) function).
-+ * onDropStyle
-+ *     This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
-+ *     to what you can do. Also this replaces the original style, so again consider using onDragClass which
-+ *     is simply added and then removed on drop.
-+ * onDragClass
-+ *     This class is added for the duration of the drag and then removed when the row is dropped. It is more
-+ *     flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
-+ *     is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
-+ *     stylesheet.
-+ * onDrop
-+ *     Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
-+ *     and the row that was dropped. You can work out the new order of the rows by using
-+ *     table.rows.
-+ * onDragStart
-+ *     Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
-+ *     table and the row which the user has started to drag.
-+ * onAllowDrop
-+ *     Pass a function that will be called as a row is over another row. If the function returns true, allow 
-+ *     dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
-+ *     the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
-+ * scrollAmount
-+ *     This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
-+ *     window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
-+ *     FF3 beta
-+ * dragHandle
-+ *     This is the name of a class that you assign to one or more cells in each row that is draggable. If you
-+ *     specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
-+ *     will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
-+ *     the whole row is draggable.
-+ * 
-+ * Other ways to control behaviour:
-+ *
-+ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
-+ * that you don't want to be draggable.
-+ *
-+ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
-+ * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
-+ * an ID as must all the rows.
-+ *
-+ * Other methods:
-+ *
-+ * $("...").tableDnDUpdate() 
-+ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
-+ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
-+ * The table maintains the original configuration (so you don't have to specify it again).
-+ *
-+ * $("...").tableDnDSerialize()
-+ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
-+ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
-+ *
-+ * Known problems:
-+ * - Auto-scoll has some problems with IE7  (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
-+ * 
-+ * Version 0.2: 2008-02-20 First public version
-+ * Version 0.3: 2008-02-07 Added onDragStart option
-+ *                         Made the scroll amount configurable (default is 5 as before)
-+ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
-+ *                         Added onAllowDrop to control dropping
-+ *                         Fixed a bug which meant that you couldn't set the scroll amount in both directions
-+ *                         Added serialize method
-+ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
-+ *                         draggable
-+ *                         Improved the serialize method to use a default (and settable) regular expression.
-+ *                         Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
-+ */
-+jQuery.tableDnD = {
-+    /** Keep hold of the current table being dragged */
-+    currentTable : null,
-+    /** Keep hold of the current drag object if any */
-+    dragObject: null,
-+    /** The current mouse offset */
-+    mouseOffset: null,
-+    /** Remember the old value of Y so that we don't do too much processing */
-+    oldY: 0,
-+
-+    /** Actually build the structure */
-+    build: function(options) {
-+        // Set up the defaults if any
-+
-+        this.each(function() {
-+            // This is bound to each matching table, set up the defaults and override with user options
-+            this.tableDnDConfig = jQuery.extend({
-+                onDragStyle: null,
-+                onDropStyle: null,
-+                              // Add in the default class for whileDragging
-+                              onDragClass: "tDnD_whileDrag",
-+                onDrop: null,
-+                onDragStart: null,
-+                scrollAmount: 5,
-+                              serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
-+                              serializeParamName: null, // If you want to specify another parameter name instead of the table ID
-+                dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
-+            }, options || {});
-+            // Now make the rows draggable
-+            jQuery.tableDnD.makeDraggable(this);
-+        });
-+
-+        // Now we need to capture the mouse up and mouse move event
-+        // We can use bind so that we don't interfere with other event handlers
-+        jQuery(document)
-+            .bind('mousemove', jQuery.tableDnD.mousemove)
-+            .bind('mouseup', jQuery.tableDnD.mouseup);
-+
-+        // Don't break the chain
-+        return this;
-+    },
-+
-+    /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
-+    makeDraggable: function(table) {
-+        var config = table.tableDnDConfig;
-+              if (table.tableDnDConfig.dragHandle) {
-+                      // We only need to add the event to the specified cells
-+                      var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
-+                      cells.each(function() {
-+                              // The cell is bound to "this"
-+                jQuery(this).mousedown(function(ev) {
-+                    jQuery.tableDnD.dragObject = this.parentNode;
-+                    jQuery.tableDnD.currentTable = table;
-+                    jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
-+                    if (config.onDragStart) {
-+                        // Call the onDrop method if there is one
-+                        config.onDragStart(table, this);
-+                    }
-+                    return false;
-+                });
-+                      })
-+              } else {
-+                      // For backwards compatibility, we add the event to the whole row
-+              var rows = jQuery("tr", table); // get all the rows as a wrapped set
-+              rows.each(function() {
-+                              // Iterate through each row, the row is bound to "this"
-+                              var row = jQuery(this);
-+                              if (! row.hasClass("nodrag")) {
-+                      row.mousedown(function(ev) {
-+                          if (ev.target.tagName == "TD") {
-+                              jQuery.tableDnD.dragObject = this;
-+                              jQuery.tableDnD.currentTable = table;
-+                              jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
-+                              if (config.onDragStart) {
-+                                  // Call the onDrop method if there is one
-+                                  config.onDragStart(table, this);
-+                              }
-+                              return false;
-+                          }
-+                      }).css("cursor", "move"); // Store the tableDnD object
-+                              }
-+                      });
-+              }
-+      },
-+
-+      updateTables: function() {
-+              this.each(function() {
-+                      // this is now bound to each matching table
-+                      if (this.tableDnDConfig) {
-+                              jQuery.tableDnD.makeDraggable(this);
-+                      }
-+              })
-+      },
-+
-+    /** Get the mouse coordinates from the event (allowing for browser differences) */
-+    mouseCoords: function(ev){
-+        if(ev.pageX || ev.pageY){
-+            return {x:ev.pageX, y:ev.pageY};
-+        }
-+        return {
-+            x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
-+            y:ev.clientY + document.body.scrollTop  - document.body.clientTop
-+        };
-+    },
-+
-+    /** Given a target element and a mouse event, get the mouse offset from that element.
-+        To do this we need the element's position and the mouse position */
-+    getMouseOffset: function(target, ev) {
-+        ev = ev || window.event;
-+
-+        var docPos    = this.getPosition(target);
-+        var mousePos  = this.mouseCoords(ev);
-+        return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
-+    },
-+
-+    /** Get the position of an element by going up the DOM tree and adding up all the offsets */
-+    getPosition: function(e){
-+        var left = 0;
-+        var top  = 0;
-+        /** Safari fix -- thanks to Luis Chato for this! */
-+        if (e.offsetHeight == 0) {
-+            /** Safari 2 doesn't correctly grab the offsetTop of a table row
-+            this is detailed here:
-+            http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
-+            the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
-+            note that firefox will return a text node as a first child, so designing a more thorough
-+            solution may need to take that into account, for now this seems to work in firefox, safari, ie */
-+            e = e.firstChild; // a table cell
-+        }
-+
-+        while (e.offsetParent){
-+            left += e.offsetLeft;
-+            top  += e.offsetTop;
-+            e     = e.offsetParent;
-+        }
-+
-+        left += e.offsetLeft;
-+        top  += e.offsetTop;
-+
-+        return {x:left, y:top};
-+    },
-+
-+    mousemove: function(ev) {
-+        if (jQuery.tableDnD.dragObject == null) {
-+            return;
-+        }
-+
-+        var dragObj = jQuery(jQuery.tableDnD.dragObject);
-+        var config = jQuery.tableDnD.currentTable.tableDnDConfig;
-+        var mousePos = jQuery.tableDnD.mouseCoords(ev);
-+        var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
-+        //auto scroll the window
-+          var yOffset = window.pageYOffset;
-+              if (document.all) {
-+              // Windows version
-+              //yOffset=document.body.scrollTop;
-+              if (typeof document.compatMode != 'undefined' &&
-+                   document.compatMode != 'BackCompat') {
-+                 yOffset = document.documentElement.scrollTop;
-+              }
-+              else if (typeof document.body != 'undefined') {
-+                 yOffset=document.body.scrollTop;
-+              }
-+
-+          }
-+                  
-+              if (mousePos.y-yOffset < config.scrollAmount) {
-+              window.scrollBy(0, -config.scrollAmount);
-+          } else {
-+            var windowHeight = window.innerHeight ? window.innerHeight
-+                    : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
-+            if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
-+                window.scrollBy(0, config.scrollAmount);
-+            }
-+        }
-+
-+
-+        if (y != jQuery.tableDnD.oldY) {
-+            // work out if we're going up or down...
-+            var movingDown = y > jQuery.tableDnD.oldY;
-+            // update the old value
-+            jQuery.tableDnD.oldY = y;
-+            // update the style to show we're dragging
-+                      if (config.onDragClass) {
-+                              dragObj.addClass(config.onDragClass);
-+                      } else {
-+                  dragObj.css(config.onDragStyle);
-+                      }
-+            // If we're over a row then move the dragged row to there so that the user sees the
-+            // effect dynamically
-+            var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
-+            if (currentRow) {
-+                // TODO worry about what happens when there are multiple TBODIES
-+                if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
-+                    jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
-+                } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
-+                    jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
-+                }
-+            }
-+        }
-+
-+        return false;
-+    },
-+
-+    /** We're only worried about the y position really, because we can only move rows up and down */
-+    findDropTargetRow: function(draggedRow, y) {
-+        var rows = jQuery.tableDnD.currentTable.rows;
-+        for (var i=0; i<rows.length; i++) {
-+            var row = rows[i];
-+            var rowY    = this.getPosition(row).y;
-+            var rowHeight = parseInt(row.offsetHeight)/2;
-+            if (row.offsetHeight == 0) {
-+                rowY = this.getPosition(row.firstChild).y;
-+                rowHeight = parseInt(row.firstChild.offsetHeight)/2;
-+            }
-+            // Because we always have to insert before, we need to offset the height a bit
-+            if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
-+                // that's the row we're over
-+                              // If it's the same as the current row, ignore it
-+                              if (row == draggedRow) {return null;}
-+                var config = jQuery.tableDnD.currentTable.tableDnDConfig;
-+                if (config.onAllowDrop) {
-+                    if (config.onAllowDrop(draggedRow, row)) {
-+                        return row;
-+                    } else {
-+                        return null;
-+                    }
-+                } else {
-+                                      // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
-+                    var nodrop = jQuery(row).hasClass("nodrop");
-+                    if (! nodrop) {
-+                        return row;
-+                    } else {
-+                        return null;
-+                    }
-+                }
-+                return row;
-+            }
-+        }
-+        return null;
-+    },
-+
-+    mouseup: function(e) {
-+        if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
-+            var droppedRow = jQuery.tableDnD.dragObject;
-+            var config = jQuery.tableDnD.currentTable.tableDnDConfig;
-+            // If we have a dragObject, then we need to release it,
-+            // The row will already have been moved to the right place so we just reset stuff
-+                      if (config.onDragClass) {
-+                  jQuery(droppedRow).removeClass(config.onDragClass);
-+                      } else {
-+                  jQuery(droppedRow).css(config.onDropStyle);
-+                      }
-+            jQuery.tableDnD.dragObject   = null;
-+            if (config.onDrop) {
-+                // Call the onDrop method if there is one
-+                config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
-+            }
-+            jQuery.tableDnD.currentTable = null; // let go of the table too
-+        }
-+    },
-+
-+    serialize: function() {
-+        if (jQuery.tableDnD.currentTable) {
-+            return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
-+        } else {
-+            return "Error: No Table id set, you need to set an id on your table and every row";
-+        }
-+    },
-+
-+      serializeTable: function(table) {
-+        var result = "";
-+        var tableId = table.id;
-+        var rows = table.rows;
-+        for (var i=0; i<rows.length; i++) {
-+            if (result.length > 0) result += "&";
-+            var rowId = rows[i].id;
-+            if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
-+                rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
-+            }
-+
-+            result += tableId + '[]=' + rowId;
-+        }
-+        return result;
-+      },
-+
-+      serializeTables: function() {
-+        var result = "";
-+        this.each(function() {
-+                      // this is now bound to each matching table
-+                      result += jQuery.tableDnD.serializeTable(this);
-+              });
-+        return result;
-+    }
-+
-+}
-+
-+jQuery.fn.extend(
-+      {
-+              tableDnD : jQuery.tableDnD.build,
-+              tableDnDUpdate : jQuery.tableDnD.updateTables,
-+              tableDnDSerialize: jQuery.tableDnD.serializeTables
-+      }
-+);
-\ No newline at end of file
---- eventum-2.2/htdocs/list.php        2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/htdocs/list.php  2009-10-12 22:10:36.435851675 +0300
-@@ -53,6 +53,11 @@
-             $profile['sort_by'] . "&sort_order=" . $profile['sort_order']);
- }
-+@$reorder_usr_id = $_REQUEST["reorder_user"];
-+@$reorder_issue_id = $_REQUEST["reorder_source"];
-+@$reorder_neworder = $_REQUEST["reorder_neworder"];
-+Issue::reorderUserIssues($reorder_usr_id, $reorder_issue_id, $reorder_neworder);
-+
- $options = Issue::saveSearchParams();
- $tpl->assign("options", $options);
- $tpl->assign("sorting", Issue::getSortingInfo($options));
-@@ -78,6 +83,21 @@
- }
- $assign_options += $users;
-+// get the isu_order (assignated users) ordering user
-+if (!empty($options["users"])) {
-+    if ($options["users"] == -2) {
-+        $isu_order_user = $usr_id;
-+    } else
-+    if ($options["users"] > 0) {
-+        $isu_order_user = $options["users"];
-+    } else {
-+        unset($isu_order_user);
-+    }
-+} else {
-+    unset($isu_order_user);
-+}
-+$tpl->assign("isu_order_user", $isu_order_user);
-+
- $list = Issue::getListing($prj_id, $options, $pagerRow, $rows);
- $tpl->assign("list", $list["list"]);
- $tpl->assign("list_info", $list["info"]);
---- eventum-2.2/htdocs/list.php.~1~    1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/htdocs/list.php.~1~      2009-09-14 18:07:55.000000000 +0300
-@@ -0,0 +1,116 @@
-+<?php
-+/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
-+// +----------------------------------------------------------------------+
-+// | Eventum - Issue Tracking System                                      |
-+// +----------------------------------------------------------------------+
-+// | Copyright (c) 2003 - 2008 MySQL AB                                   |
-+// | Copyright (c) 2008 - 2009 Sun Microsystem Inc.                       |
-+// |                                                                      |
-+// | This program is free software; you can redistribute it and/or modify |
-+// | it under the terms of the GNU General Public License as published by |
-+// | the Free Software Foundation; either version 2 of the License, or    |
-+// | (at your option) any later version.                                  |
-+// |                                                                      |
-+// | This program is distributed in the hope that it will be useful,      |
-+// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
-+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
-+// | GNU General Public License for more details.                         |
-+// |                                                                      |
-+// | You should have received a copy of the GNU General Public License    |
-+// | along with this program; if not, write to:                           |
-+// |                                                                      |
-+// | Free Software Foundation, Inc.                                       |
-+// | 59 Temple Place - Suite 330                                          |
-+// | Boston, MA 02111-1307, USA.                                          |
-+// +----------------------------------------------------------------------+
-+// | Authors: João Prado Maia <jpm@mysql.com>                             |
-+// +----------------------------------------------------------------------+
-+//
-+// @(#) $Id$
-+
-+require_once dirname(__FILE__) . '/../init.php';
-+
-+$tpl = new Template_Helper();
-+$tpl->setTemplate("list.tpl.html");
-+
-+Auth::checkAuthentication(APP_COOKIE);
-+$usr_id = Auth::getUserID();
-+$prj_id = Auth::getCurrentProject();
-+
-+$pagerRow = Issue::getParam('pagerRow');
-+if (empty($pagerRow)) {
-+    $pagerRow = 0;
-+}
-+$rows = Issue::getParam('rows');
-+if (empty($rows)) {
-+    $rows = APP_DEFAULT_PAGER_SIZE;
-+}
-+
-+if (@$_REQUEST['view'] == 'my_assignments') {
-+    $profile = Search_Profile::getProfile($usr_id, $prj_id, 'issue');
-+    Search_Profile::remove($usr_id, $prj_id, 'issue');
-+    Auth::redirect("list.php?users=$usr_id&hide_closed=1&rows=$rows&sort_by=" .
-+            $profile['sort_by'] . "&sort_order=" . $profile['sort_order']);
-+}
-+
-+$options = Issue::saveSearchParams();
-+$tpl->assign("options", $options);
-+$tpl->assign("sorting", Issue::getSortingInfo($options));
-+
-+// generate options for assign list. If there are groups and user is above a customer, include groups
-+$groups = Group::getAssocList($prj_id);
-+$users = Project::getUserAssocList($prj_id, 'active', User::getRoleID('Customer'));
-+$assign_options = array(
-+    ""      =>  ev_gettext("Any"),
-+    "-1"    =>  ev_gettext("un-assigned"),
-+    "-2"    =>  ev_gettext("myself and un-assigned")
-+);
-+if (Auth::isAnonUser())
-+    unset($assign_options["-2"]);
-+else if (User::getGroupID($usr_id) != '') {
-+    $assign_options['-3'] = ev_gettext('myself and my group');
-+    $assign_options['-4'] = ev_gettext('myself, un-assigned and my group');
-+}
-+if ((count($groups) > 0) && (Auth::getCurrentRole() > User::getRoleID("Customer"))) {
-+    foreach ($groups as $grp_id => $grp_name) {
-+        $assign_options["grp:$grp_id"] = ev_gettext("Group") . ": " . $grp_name;
-+    }
-+}
-+$assign_options += $users;
-+
-+$list = Issue::getListing($prj_id, $options, $pagerRow, $rows);
-+$tpl->assign("list", $list["list"]);
-+$tpl->assign("list_info", $list["info"]);
-+$tpl->assign("csv_data", base64_encode(@$list["csv"]));
-+
-+$tpl->assign("columns", Display_Column::getColumnsToDisplay($prj_id, 'list_issues'));
-+$tpl->assign("priorities", Priority::getAssocList($prj_id));
-+$tpl->assign("status", Status::getAssocStatusList($prj_id));
-+$tpl->assign("assign_options", $assign_options);
-+$tpl->assign("custom", Filter::getAssocList($prj_id));
-+$tpl->assign("csts", Filter::getListing(true));
-+$tpl->assign("active_filters", Filter::getActiveFilters($options));
-+$tpl->assign("categories", Category::getAssocList($prj_id));
-+$tpl->assign("releases", Release::getAssocList($prj_id, true));
-+$tpl->assign("reporters", Project::getReporters($prj_id));
-+
-+$prefs = Prefs::get($usr_id);
-+$tpl->assign("refresh_rate", $prefs['list_refresh_rate'] * 60);
-+$tpl->assign("refresh_page", "list.php");
-+
-+// items needed for bulk update tool
-+if (Auth::getCurrentRole() > User::getRoleID("Developer")) {
-+    $tpl->assign("users", $users);
-+
-+    if (Workflow::hasWorkflowIntegration($prj_id)) {
-+        $open_statuses = Workflow::getAllowedStatuses($prj_id);
-+    } else {
-+        $open_statuses = Status::getAssocStatusList($prj_id, false);
-+    }
-+
-+    $tpl->assign("open_status", $open_statuses);
-+    $tpl->assign("closed_status", Status::getClosedAssocList($prj_id));
-+    $tpl->assign("available_releases", Release::getAssocList($prj_id));
-+}
-+
-+$tpl->displayTemplate();
---- eventum-2.2/lib/eventum/class.display_column.php   2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/lib/eventum/class.display_column.php     2009-10-12 22:10:36.429185594 +0300
-@@ -230,7 +230,10 @@
-                 ),
-                 "iss_expected_resolution_date"  =>  array(
-                     "title" =>  ev_gettext("Expected Resolution Date")
--                )
-+                ),
-+                "isu_order" => array(
-+                    "title" => ev_gettext("Order")
-+                ),
-             )
-         );
-         return $columns[$page];
---- eventum-2.2/lib/eventum/class.display_column.php.~1~       1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/lib/eventum/class.display_column.php.~1~ 2009-09-14 18:07:55.000000000 +0300
-@@ -0,0 +1,318 @@
-+<?php
-+/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
-+// +----------------------------------------------------------------------+
-+// | Eventum - Issue Tracking System                                      |
-+// +----------------------------------------------------------------------+
-+// | Copyright (c) 2003 - 2008 MySQL AB                                   |
-+// | Copyright (c) 2008 - 2009 Sun Microsystem Inc.                       |
-+// |                                                                      |
-+// | This program is free software; you can redistribute it and/or modify |
-+// | it under the terms of the GNU General Public License as published by |
-+// | the Free Software Foundation; either version 2 of the License, or    |
-+// | (at your option) any later version.                                  |
-+// |                                                                      |
-+// | This program is distributed in the hope that it will be useful,      |
-+// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
-+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
-+// | GNU General Public License for more details.                         |
-+// |                                                                      |
-+// | You should have received a copy of the GNU General Public License    |
-+// | along with this program; if not, write to:                           |
-+// |                                                                      |
-+// | Free Software Foundation, Inc.                                       |
-+// | 59 Temple Place - Suite 330                                          |
-+// | Boston, MA 02111-1307, USA.                                          |
-+// +----------------------------------------------------------------------+
-+// | Authors: Bryan Alsdorf <bryan@mysql.com>                             |
-+// +----------------------------------------------------------------------+
-+//
-+//
-+
-+
-+/**
-+ * Class to handle determining which columns should be displayed and in what order
-+ * on a page (e.g. Issue Listing page).
-+ *
-+ * @author Bryan Alsdorf <bryan@mysql.com>
-+ * @version 1.0
-+ */
-+
-+class Display_Column
-+{
-+    /**
-+     * Returns the columns that should be displayed for the specified page.
-+     * This method will remove columns that should not be displayed, due to
-+     * lack of customer integration or insufficient role.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The ID of the project.
-+     * @param   string $page The page to return columns for.
-+     * @return  array An array of columns that should be displayed.
-+     */
-+    function getColumnsToDisplay($prj_id, $page)
-+    {
-+        static $returns;
-+
-+        // poor man's caching system
-+        if (!empty($returns[$prj_id][$page])) {
-+            return $returns[$prj_id][$page];
-+        }
-+
-+        $current_role = Auth::getCurrentRole();
-+        $data = self::getSelectedColumns($prj_id, $page);
-+        $has_customer_integration = Customer::hasCustomerIntegration($prj_id);
-+        $only_with_customers = array('iss_customer_id', 'support_level');
-+
-+        // remove groups if there are no groups in the system.
-+        if (count(Group::getAssocList($prj_id)) < 1) {
-+            unset($data['iss_grp_id']);
-+        }
-+        // remove category column if there are no categories in the system
-+        if (count(Category::getAssocList($prj_id)) < 1) {
-+            unset($data['prc_title']);
-+        }
-+        // remove custom fields column if there are no custom fields
-+        if (count(Custom_Field::getFieldsToBeListed($prj_id)) < 1) {
-+            unset($data['custom_fields']);
-+        }
-+        // remove customer field if user has a role of customer
-+        if ($current_role == User::getRoleID("Customer")) {
-+            unset($data['iss_customer_id']);
-+        }
-+
-+        foreach ($data as $field => $info) {
-+            // remove fields based on role
-+            if ($info['min_role'] > $current_role) {
-+                unset($data[$field]);
-+                continue;
-+            }
-+            // remove fields based on customer integration
-+            if (!$has_customer_integration && (in_array($field, $only_with_customers))) {
-+                unset($data[$field]);
-+                continue;
-+            }
-+            // get title
-+            $data[$field] = self::getColumnInfo($page, $field);
-+        }
-+        $returns[$prj_id][$page] = $data;
-+        return $data;
-+    }
-+
-+
-+    /**
-+     * Returns the columns that have been selected to be displayed on the specified page. This list
-+     * contains all selected columns, even if they won't actually be displayed.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The ID of the project.
-+     * @param   string $page The page to return columns for.
-+     * @return  array An array of columns that should be displayed.
-+     */
-+    function getSelectedColumns($prj_id, $page)
-+    {
-+        static $returns;
-+
-+        // poor man's caching system
-+        if (!empty($returns[$prj_id][$page])) {
-+            return $returns[$prj_id][$page];
-+        }
-+
-+        $stmt = "SELECT
-+                    ctd_field,
-+                    ctd_min_role,
-+                    ctd_rank
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "columns_to_display
-+                WHERE
-+                    ctd_prj_id = $prj_id AND
-+                    ctd_page = '$page'
-+                ORDER BY
-+                    ctd_rank";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt, false, array(), DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$prj_id][$page] = array();
-+            foreach ($res as $field_name => $row) {
-+                $returns[$prj_id][$page][$field_name] = self::getColumnInfo($page, $field_name);
-+                $returns[$prj_id][$page][$field_name]['min_role'] = $row['ctd_min_role'];
-+                $returns[$prj_id][$page][$field_name]['rank'] = $row['ctd_rank'];
-+            }
-+            return $returns[$prj_id][$page];
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the info of the column
-+     *
-+     * @access  public
-+     * @param   string $page The name of the page.
-+     * @param   string $column The name of the column
-+     * @return  string Info on the column
-+     */
-+    function getColumnInfo($page, $column)
-+    {
-+        $columns = self::getAllColumns($page);
-+        return $columns[$column];
-+    }
-+
-+
-+    /**
-+     * Returns all columns available for a page
-+     *
-+     * @access  public
-+     * @param   string $page The name of the page
-+     * @return  array An array of columns
-+     */
-+    function getAllColumns($page)
-+    {
-+        $columns = array(
-+            "list_issues"   =>  array(
-+                "pri_rank"    =>  array(
-+                    "title" =>  ev_gettext("Priority")
-+                ),
-+                "iss_id"    =>  array(
-+                    "title" =>  ev_gettext("Issue ID")
-+                ),
-+                "usr_full_name" =>  array(
-+                    "title" =>  ev_gettext("Reporter")
-+                ),
-+                "iss_created_date"    =>  array(
-+                    "title" =>  ev_gettext("Created Date")
-+                ),
-+                "iss_grp_id"    =>  array(
-+                    "title" =>  ev_gettext("Group")
-+                ),
-+                "assigned"  =>  array(
-+                    "title" =>  ev_gettext("Assigned")
-+                ),
-+                "time_spent"    =>  array(
-+                    "title" =>  ev_gettext("Time Spent")
-+                ),
-+                "iss_percent_complete"    =>  array(
-+                    "title" =>  ev_gettext("% Complete"),
-+                    "default_role"  =>  9
-+                ),
-+                "iss_dev_time"    =>  array(
-+                    "title" =>  ev_gettext("Est Dev Time"),
-+                    "default_role"  =>  9
-+                ),
-+                "prc_title"     =>  array(
-+                    "title" =>  ev_gettext("Category")
-+                ),
-+                "pre_title" =>  array(
-+                    "title" =>  ev_gettext("Release")
-+                ),
-+                "iss_customer_id"   =>  array(
-+                    "title" =>  ev_gettext("Customer")
-+                ),
-+                "support_level" =>  array(
-+                    "title" =>  ev_gettext("Support Level")
-+                ),
-+                "sta_rank"    =>  array(
-+                    "title" =>  ev_gettext("Status")
-+                ),
-+                "sta_change_date"   =>  array(
-+                    "title" =>  ev_gettext("Status Change Date")
-+                ),
-+                "last_action_date"  =>  array(
-+                    "title" =>  ev_gettext("Last Action Date")
-+                ),
-+                "custom_fields" =>  array(
-+                    "title" =>  ev_gettext("Custom Fields")
-+                ),
-+                "iss_summary"   =>  array(
-+                    "title" =>  ev_gettext("Summary"),
-+                    "align" =>  "left",
-+                    "width" =>  '30%'
-+                ),
-+                "iss_expected_resolution_date"  =>  array(
-+                    "title" =>  ev_gettext("Expected Resolution Date")
-+                )
-+            )
-+        );
-+        return $columns[$page];
-+    }
-+
-+
-+    /**
-+     * Saves settings on which columns should be displayed.
-+     *
-+     * @access  public
-+     * @return  integer 1 if settings were saved successfully, -1 if there was an error.
-+     */
-+    function save()
-+    {
-+        $page = Misc::escapeString($_REQUEST['page']);
-+        $prj_id = Misc::escapeInteger($_REQUEST['prj_id']);
-+
-+        $ranks = $_REQUEST['rank'];
-+        asort($ranks);
-+
-+        // delete current entries
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "columns_to_display
-+                WHERE
-+                    ctd_prj_id = $prj_id AND
-+                    ctd_page = '$page'";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        $rank = 1;
-+        foreach ($ranks as $field_name => $requested_rank) {
-+            $sql = "INSERT INTO
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "columns_to_display
-+                    SET
-+                        ctd_prj_id = $prj_id,
-+                        ctd_page = '$page',
-+                        ctd_field = '$field_name',
-+                        ctd_min_role = " . $_REQUEST['min_role'][$field_name] . ",
-+                        ctd_rank = $rank";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+            $rank++;
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Adds records in database for new project.
-+     *
-+     * @param   integer $prj_id The ID of the project.
-+     */
-+    function setupNewProject($prj_id)
-+    {
-+        $page = 'list_issues';
-+        $columns = self::getAllColumns($page);
-+        $rank = 1;
-+        foreach ($columns as $field_name => $column) {
-+            if (!empty($column['default_role'])) {
-+                $min_role = $column['default_role'];
-+            } else {
-+                $min_role = 1;
-+            }
-+            $stmt = "INSERT INTO
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "columns_to_display
-+                     SET
-+                        ctd_prj_id = $prj_id,
-+                        ctd_page = '$page',
-+                        ctd_field = '$field_name',
-+                        ctd_min_role = $min_role,
-+                        ctd_rank = $rank";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+            $rank++;
-+        }
-+    }
-+}
---- eventum-2.2/lib/eventum/class.issue.php    2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/lib/eventum/class.issue.php      2009-10-12 22:10:36.445851670 +0300
-@@ -1333,6 +1333,7 @@
-             Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-             return -1;
-         } else {
-+            self::moveOrderForAllUsers($issue_id, 1000);
-             $prj_id = self::getProjectID($issue_id);
-             // record the change
-@@ -1636,6 +1637,180 @@
-         }
-     }
-+    /**
-+     * Method used to update the a single detail field of a specific issue.
-+     *
-+     * @param integer $issue_id
-+     * @param string $field_name
-+     * @param string $field_value
-+     * @param string $field_type string or integer (for escape)
-+     * @return integer 1 on success, -1 otherwise
-+     */
-+    function updateField($issue_id, $field_name, $filed_value) {
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $usr_id = Auth::getUserID();
-+        $prj_id = self::getProjectID($issue_id);
-+
-+        // get all of the 'current' information of this issue
-+        $current = self::getDetails($issue_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='updated'";
-+
-+        switch ($field_name) {
-+            case 'category':
-+                $stmt .= ", iss_prc_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'release':
-+                $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'expected_resolution_date':
-+                if (is_null($filed_value)) {
-+                    $stmt .= ", iss_expected_resolution_date = null";
-+                } else {
-+                    $stmt .= ", iss_expected_resolution_date = '" . Misc::escapeString($filed_value) . "'";
-+                }
-+            break;
-+            case 'release':
-+                $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'priority':
-+                $stmt .= ", iss_pri_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'status':
-+                $stmt .= ", iss_sta_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'resolution':
-+                $stmt .= ", iss_res_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'summary':
-+                $stmt .= ", iss_summary = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'description':
-+                $stmt .= ", iss_description = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'estimated_dev_time':
-+                $stmt .= ", iss_dev_time = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'percent_complete':
-+                $stmt .= ", iss_percent_complete = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'trigger_reminders':
-+                $stmt .= ", iss_trigger_reminders = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'group':
-+                $stmt .= ", iss_grp_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'private':
-+                $stmt .= ", iss_private = " . Misc::escapeInteger($filed_value);
-+            break;
-+            default:
-+                Error_Handler::logError("Unknown field name $field_name", __FILE__, __LINE__);
-+                return -1;
-+            break;
-+        }
-+
-+        $stmt .= "
-+                 WHERE
-+                    iss_id=$issue_id";
-+
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $new = array(
-+                'category' => $current['iss_prc_id'],
-+                'release' => $current['iss_pre_id'],
-+                'expected_resolution_date' => $current['iss_expected_resolution_date'],
-+                'release' => $current['iss_pre_id'],
-+                'priority' => $current['iss_pri_id'],
-+                'status' => $current['iss_sta_id'],
-+                'resolution' => $current['iss_res_id'],
-+                'summary' => $current['iss_summary'],
-+                'description' => $current['iss_description'],
-+                'estimated_dev_time' => $current['iss_dev_time'],
-+                'percent_complete' => $current['iss_percent_complete'],
-+                'trigger_reminders' => $current['iss_trigger_reminders'],
-+                'group' => $current['iss_grp_id'],
-+                'iss_private' => $current['private']
-+            );
-+            $new[$field_name] = $filed_value;
-+
-+            // add change to the history (only for changes on specific fields?)
-+            $updated_fields = array();
-+            if ($field_name == 'expected_resolution_date' && $current["iss_expected_resolution_date"] != $filed_value) {
-+                $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $filed_value);
-+            }
-+            if ($field_name == 'category' && $current["iss_prc_id"] != $filed_value) {
-+                $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($filed_value));
-+            }
-+            if ($field_name == 'release' && $current["iss_pre_id"] != $filed_value) {
-+                $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($filed_value));
-+            }
-+            if ($field_name == 'priority' && $current["iss_pri_id"] != $filed_value) {
-+                $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($filed_value));
-+                Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $new);
-+            }
-+            if ($field_name == 'status' && $current["iss_sta_id"] != $filed_value) {
-+                // clear out the last-triggered-reminder flag when changing the status of an issue
-+                Reminder_Action::clearLastTriggered($issue_id);
-+
-+                // if old status was closed and new status is not, clear closed data from issue.
-+                $old_status_details = Status::getDetails($current['iss_sta_id']);
-+                if ($old_status_details['sta_is_closed'] == 1) {
-+                    $new_status_details = Status::getDetails($filed_value);
-+                    if ($new_status_details['sta_is_closed'] != 1) {
-+                        self::clearClosed($issue_id);
-+                    }
-+                }
-+                $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($filed_value));
-+            }
-+            if ($field_name == 'resolution' && $current["iss_res_id"] != $filed_value) {
-+                $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($filed_value));
-+            }
-+            if ($field_name == 'estimated_dev_time' && $current["iss_dev_time"] != $filed_value) {
-+                $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($filed_value*60)));
-+            }
-+            if ($field_name == 'summary' && $current["iss_summary"] != $filed_value) {
-+                $updated_fields["Summary"] = '';
-+            }
-+            if ($field_name == 'description' && $current["iss_description"] != $filed_value) {
-+                $updated_fields["Description"] = '';
-+            }
-+            if ($field_name == 'private' && ($filed_value != $current['iss_private'])) {
-+                $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($filed_value));
-+            }
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $i = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($i > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    if (($key != "Summary") && ($key != "Description")) {
-+                        $changes .= "$key: $value";
-+                    } else {
-+                        $changes .= "$key";
-+                    }
-+                    $i++;
-+                }
-+
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
-+                // send notifications for the issue being updated
-+                Notification::notifyIssueUpdated($issue_id, $current, $new);
-+            }
-+        }
-+        return 1;
-+    }
-+
-     /**
-      * Move the issue to a new project
-@@ -1800,16 +1975,33 @@
-     {
-         $issue_id = Misc::escapeInteger($issue_id);
-         $assignee_usr_id = Misc::escapeInteger($assignee_usr_id);
-+        $order = 1;
-+        // move all orders down to free "order space" for this new association
-+        $stmt = "UPDATE 
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 SET
-+                    isu_order = isu_order + 1
-+                 WHERE
-+                    isu_usr_id = $assignee_usr_id AND
-+                    isu_order >= $order";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        // insert the new association
-         $stmt = "INSERT INTO
-                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-                  (
-                     isu_iss_id,
-                     isu_usr_id,
--                    isu_assigned_date
-+                    isu_assigned_date,
-+                    isu_order
-                  ) VALUES (
-                     $issue_id,
-                     $assignee_usr_id,
--                    '" . Date_Helper::getCurrentDateGMT() . "'
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    $order
-                  )";
-         $res = DB_Helper::getInstance()->query($stmt);
-         if (PEAR::isError($res)) {
-@@ -1824,6 +2016,78 @@
-         }
-     }
-+    /**
-+     * Method used to get the order list to be rearranged
-+     * 
-+     * @access  private
-+     * @param   string $issue_id The issue ID or a comma seperated list of IDs already prepared for giving to mysql
-+     * @param   string $usr_id The user to remove. When not specified, all users are taken as to be removed for that issue
-+     * @return  mixed delete order list to be rearranged. Used as a parameter to the method of rearranging the order.
-+     */
-+    function getDeleteUserAssociationOrderList($issue_id, $usr_id = "")
-+    {
-+        // find all affected associantion orders
-+        $stmt = "SELECT isu_usr_id, isu_order FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                 isu_iss_id IN ($issue_id)";
-+        if (!empty($usr_id)) {
-+            $stmt.= " AND isu_usr_id IN ($usr_id)";
-+        }
-+        $stmt.= "ORDER BY isu_order";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $deleted_orders = array();
-+            foreach ($res as $row) {
-+                if (empty($deleted_orders[$row['isu_usr_id']])) {
-+                    $deleted_orders[$row['isu_usr_id']] = array();
-+                }
-+                $deleted_orders[$row['isu_usr_id']] [] = $row['isu_order'];
-+            }
-+            return $deleted_orders;
-+        }
-+    }
-+
-+    /**
-+     *
-+     * Method used to rearrange order list in the db according to known deleted records
-+     *
-+     * @access  private
-+     * @param   mixed  deleteorder list
-+     * @return void
-+     */
-+    function rearrangeDeleteUserAssociationOrderList($delete_order_list)
-+    {
-+        if (empty($delete_order_list) || (!is_array($delete_order_list))) {
-+            return -1;
-+        }
-+        foreach ($delete_order_list as $isu_usr_id => $orders) {
-+            for ($i = 0; $i < count($orders); $i++) { // traverse all deleted orders
-+                // move the orders after them up to take the "order space" of the deleted records
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                         SET
-+                            isu_order = isu_order - " . ($i+1) . "
-+                         WHERE
-+                            isu_usr_id = $isu_usr_id AND
-+                            isu_order > " . $orders[$i];
-+                if ($i < count($orders) - 1) {
-+                    $stmt.=  " AND
-+                            isu_order < " . $orders[$i+1];
-+                }
-+                $res = DB_Helper::getInstance()->query($stmt);
-+                if (PEAR::isError($res)) {
-+                    Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                    return -1;
-+                }
-+            }
-+        }
-+        return 1;
-+    }
-+
-     /**
-      * Method used to delete all user assignments for a specific issue.
-@@ -1839,6 +2103,7 @@
-         if (is_array($issue_id)) {
-             $issue_id = implode(", ", $issue_id);
-         }
-+        $deleted_order_list = self::getDeleteUserAssociationOrderList($issue_id);
-         $stmt = "DELETE FROM
-                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-                  WHERE
-@@ -1869,6 +2134,7 @@
-     {
-         $issue_id = Misc::escapeInteger($issue_id);
-         $usr_id = Misc::escapeInteger($usr_id);
-+        $delete_order_list = self::getDeleteUserAssociationOrderList($issue_id, $usr_id);
-         $stmt = "DELETE FROM
-                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-                  WHERE
-@@ -1883,6 +2149,7 @@
-                 History::add($issue_id, Auth::getUserID(), History::getTypeID('user_unassociated'),
-                     User::getFullName($usr_id) . ' removed from issue by ' . User::getFullName(Auth::getUserID()));
-             }
-+            self::rearrangeDeleteUserAssociationOrderList($delete_order_list);
-             return 1;
-         }
-     }
-@@ -2342,6 +2609,11 @@
-     {
-         $sort_by = self::getParam('sort_by');
-         $sort_order = self::getParam('sort_order');
-+        $users = self::getParam('users');
-+        if (empty($users) && ($sort_by == 'isu_order')) { // Sorting by isu_order is impossible when no user specified
-+            unset($sort_by);
-+            unset($sort_order);
-+        }
-         $rows = self::getParam('rows');
-         $hide_closed = self::getParam('hide_closed');
-         if ($hide_closed === '') {
-@@ -2448,6 +2720,7 @@
-             "iss_expected_resolution_date" => "desc",
-             "pre_title" => "asc",
-             "assigned" => "asc",
-+            "isu_order" => "desc",
-         );
-         foreach ($custom_fields as $fld_id => $fld_name) {
-@@ -3253,6 +3526,8 @@
-         $ids = implode(", ", $ids);
-         $stmt = "SELECT
-                     isu_iss_id,
-+                    isu_order,
-+                    isu_usr_id,
-                     usr_full_name
-                  FROM
-                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-@@ -3264,6 +3539,7 @@
-         if (PEAR::isError($res)) {
-             Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-         } else {
-+            // gather names of the users assigned to each issue
-             $t = array();
-             for ($i = 0; $i < count($res); $i++) {
-                 if (!empty($t[$res[$i]['isu_iss_id']])) {
-@@ -3272,9 +3548,18 @@
-                     $t[$res[$i]['isu_iss_id']] = $res[$i]['usr_full_name'];
-                 }
-             }
-+            // gather orders
-+            $o = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                if (empty($o[$res[$i]['isu_iss_id']])) {
-+                    $o[$res[$i]['isu_iss_id']] = array();
-+                }
-+                $o[$res[$i]['isu_iss_id']][$res[$i]['isu_usr_id']] = $res[$i]['isu_order'];
-+            }
-             // now populate the $result variable again
-             for ($i = 0; $i < count($result); $i++) {
-                 @$result[$i]['assigned_users'] = $t[$result[$i]['iss_id']];
-+                @$result[$i]['assigned_users_order'] = $o[$result[$i]['iss_id']];
-             }
-         }
-     }
-@@ -4247,6 +4532,7 @@
-             Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-             return -1;
-         }
-+        self::moveOrderForAllUsers($issue_id, 1);
-     }
-@@ -4346,4 +4632,120 @@
-         History::add($issue_id, Auth::getUserID(), History::getTypeID('user_associated'),
-                         "Issue assignment to changed (" . History::formatChanges(join(', ', $old_assignee_names), join(', ', $assignee_names)) . ") by " . User::getFullName(Auth::getUserID()));
-     }
-+
-+    /**
-+     * Reorders user's issues as requested by user
-+     * @access public
-+     * @param $usr_id User to be reordered
-+     * @param $issue_id Issue or array of issues to be moved
-+     * @param $neworder The new order of the issues
-+     * @return void
-+     */
-+    function reorderUserIssues($usr_id, $issue_id, $neworder)
-+    {
-+        if (!isset($usr_id) || !isset($issue_id) || !isset($neworder)) {
-+            return false;
-+        }
-+        if (!is_numeric($usr_id) || !is_numeric($neworder)) {
-+            return false;
-+        }
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $neworder = Misc::escapeInteger($neworder);
-+        if (is_array($issue_id)) {
-+            $issue_count = count($issue_id);
-+            $issue_id_str = implode(", ", $issue_id);
-+        } else {
-+            $issue_count = 1;
-+            $issue_id_str = $issue_id;
-+            $issue_id = array($issue_id);
-+        }
-+        // do a nasty pretending to be deleting stuff so that reordering happens as if these elements were deleted
-+        $orderlist = self::getDeleteUserAssociationOrderList($issue_id_str, $usr_id);
-+        self::rearrangeDeleteUserAssociationOrderList($orderlist);
-+        // move down the orders to free the "order space" needed
-+        $stmt = "UPDATE 
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 SET
-+                    isu_order = isu_order + $issue_count
-+                 WHERE
-+                    isu_usr_id = $usr_id AND
-+                    isu_order >= $neworder";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        //update the order for the issues being moved
-+        $i = 0;
-+        foreach ($issue_id as $iss_id) {
-+            $stmt = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                     SET
-+                        isu_order = " . ($neworder + $i) . "
-+                     WHERE
-+                        isu_usr_id = $usr_id AND
-+                        isu_iss_id = $iss_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+            $i++;
-+        }
-+    }
-+
-+
-+    /**
-+     * Get users issue order list
-+     * @access public
-+     * @param $user_id User
-+     * @param $order_list Order of the issues
-+     * @return void
-+     */
-+    function getIssueOrderByUser($usr_id) {
-+
-+        if (!is_numeric($usr_id)) {
-+            return false;
-+        }
-+
-+        $stmt = "SELECT
-+                    isu_iss_id, isu_order
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                WHERE
-+                    isu_usr_id = " . $usr_id ;
-+
-+        $order_list = array();
-+
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            foreach ($res as $row) {
-+                $order_list[$row["isu_iss_id"]] = $row["isu_order"];
-+            }
-+        }
-+        return $order_list;
-+    }
-+
-+    function moveOrderForAllUsers($issue_id, $neworder)
-+    {
-+        // Move the issue to the top priority for the ppl it's assigned to
-+        $stmt = "SELECT isu_usr_id FROM
-+                    "  . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        foreach ($res as $row) {
-+            self::reorderUserIssues($row["isu_usr_id"], $issue_id, $neworder);
-+        }
-+    }
-+    
- }
---- eventum-2.2/lib/eventum/class.issue.php.~1~        1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/lib/eventum/class.issue.php.~1~  2009-09-14 18:07:55.000000000 +0300
-@@ -0,0 +1,4349 @@
-+<?php
-+/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
-+// +----------------------------------------------------------------------+
-+// | Eventum - Issue Tracking System                                      |
-+// +----------------------------------------------------------------------+
-+// | Copyright (c) 2003 - 2008 MySQL AB                                   |
-+// | Copyright (c) 2008 - 2009 Sun Microsystem Inc.                       |
-+// |                                                                      |
-+// | This program is free software; you can redistribute it and/or modify |
-+// | it under the terms of the GNU General Public License as published by |
-+// | the Free Software Foundation; either version 2 of the License, or    |
-+// | (at your option) any later version.                                  |
-+// |                                                                      |
-+// | This program is distributed in the hope that it will be useful,      |
-+// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
-+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
-+// | GNU General Public License for more details.                         |
-+// |                                                                      |
-+// | You should have received a copy of the GNU General Public License    |
-+// | along with this program; if not, write to:                           |
-+// |                                                                      |
-+// | Free Software Foundation, Inc.                                       |
-+// | 59 Temple Place - Suite 330                                          |
-+// | Boston, MA 02111-1307, USA.                                          |
-+// +----------------------------------------------------------------------+
-+// | Authors: João Prado Maia <jpm@mysql.com>                             |
-+// +----------------------------------------------------------------------+
-+//
-+
-+
-+/**
-+ * Class designed to handle all business logic related to the issues in the
-+ * system, such as adding or updating them or listing them in the grid mode.
-+ *
-+ * @author  João Prado Maia <jpm@mysql.com>
-+ * @version $Revision$
-+ */
-+
-+class Issue
-+{
-+    /**
-+     * Method used to check whether a given issue ID exists or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $check_project If we should check that this issue is in the current project
-+     * @return  boolean
-+     */
-+    function exists($issue_id, $check_project = true)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        if ($check_project) {
-+            $stmt .= " AND
-+                    iss_prj_id = " . Auth::getCurrentProject();
-+        }
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of column heading titles for the
-+     * CSV export functionality of the issue listing screen.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @return  array The list of column heading titles
-+     */
-+    function getColumnHeadings($prj_id)
-+    {
-+        $headings = array(
-+            'Priority',
-+            'Issue ID',
-+            'Reporter',
-+        );
-+        // hide the group column from the output if no
-+        // groups are available in the database
-+        $groups = Group::getAssocList($prj_id);
-+        if (count($groups) > 0) {
-+            $headings[] = 'Group';
-+        }
-+        $headings[] = 'Assigned';
-+        $headings[] = 'Time Spent';
-+        // hide the category column from the output if no
-+        // categories are available in the database
-+        $categories = Category::getAssocList($prj_id);
-+        if (count($categories) > 0) {
-+            $headings[] = 'Category';
-+        }
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $headings[] = 'Customer';
-+        }
-+        $headings[] = 'Status';
-+        $headings[] = 'Status Change Date';
-+        $headings[] = 'Last Action Date';
-+        $headings[] = 'Est. Dev. TIme';
-+        $headings[] = 'Summary';
-+        $headings[] = 'Expected Resolution Date';
-+        return $headings;
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of date fields available to issues, to
-+     * be used when customizing the issue listing screen in the 'last status
-+     * change date' column.
-+     *
-+     * @access  public
-+     * @param   boolean $display_customer_fields Whether to include any customer related fields or not
-+     * @return  array The list of available date fields
-+     */
-+    function getDateFieldsAssocList($display_customer_fields = FALSE)
-+    {
-+        $fields = array(
-+            'iss_created_date'              => 'Created Date',
-+            'iss_updated_date'              => 'Last Updated Date',
-+            'iss_last_response_date'        => 'Last Response Date',
-+            'iss_closed_date'               => 'Closed Date'
-+        );
-+        if ($display_customer_fields) {
-+            $fields['iss_last_customer_action_date'] = 'Customer Action Date';
-+        }
-+        asort($fields);
-+        return $fields;
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs and their respective
-+     * titles associated to a given project.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @return  array The list of issues
-+     */
-+    function getAssocListByProject($prj_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Misc::escapeInteger($prj_id) . "
-+                 ORDER BY
-+                    iss_id ASC";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The status ID
-+     */
-+    function getStatusID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_sta_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Records the last customer action date for a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function recordLastCustomerAction($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_last_customer_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='customer action'
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the customer ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getCustomerID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the contract ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getContractID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_contract_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Sets the contract ID for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer The contract ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setContractID($issue_id, $contract_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $old_contract_id = self::getContractID($issue_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                SET
-+                    iss_customer_contract_id = " . Misc::escapeInteger($contract_id) . "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // log this
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID("contract_changed"), "Contract changed from $old_contract_id to $contract_id by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the customer ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getContactID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_contact_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the project associated to a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $force_refresh If the cache should not be used.
-+     * @return  integer The project ID
-+     */
-+    function getProjectID($issue_id, $force_refresh = false)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if ((!empty($returns[$issue_id])) && ($force_refresh != true)) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_prj_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remotely assign a given issue to an user.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing the change
-+     * @param   boolean $assignee The user ID of the assignee
-+     * @return  integer The status ID
-+     */
-+    function remoteAssign($issue_id, $usr_id, $assignee)
-+    {
-+        Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, $usr_id, self::getDetails($issue_id), array($assignee), true);
-+        // clear up the assignments for this issue, and then assign it to the current user
-+        self::deleteUserAssociations($issue_id, $usr_id);
-+        $res = self::addUserAssociation($usr_id, $issue_id, $assignee, false);
-+        if ($res != -1) {
-+            // save a history entry about this...
-+            History::add($issue_id, $usr_id, History::getTypeID('remote_assigned'), "Issue remotely assigned to " . User::getFullName($assignee) . " by " . User::getFullName($usr_id));
-+            Notification::subscribeUser($usr_id, $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'remote_assign'), false);
-+            if ($assignee != $usr_id) {
-+                Notification::notifyNewAssignment(array($assignee), $issue_id);
-+            }
-+        }
-+        return $res;
-+    }
-+
-+
-+    /**
-+     * Method used to set the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $status_id The new status ID
-+     * @param   boolean $notify If a notification should be sent about this change.
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setStatus($issue_id, $status_id, $notify = false)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+
-+        $workflow = Workflow::preStatusChange(self::getProjectID($issue_id), $issue_id, $status_id, $notify);
-+        if ($workflow !== true) {
-+            return $workflow;
-+        }
-+
-+        // check if the status is already set to the 'new' one
-+        if (self::getStatusID($issue_id) == $status_id) {
-+            return -1;
-+        }
-+
-+        $old_status = self::getStatusID($issue_id);
-+        $old_details = Status::getDetails($old_status);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_sta_id=$status_id,
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='update'
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // clear out the last-triggered-reminder flag when changing the status of an issue
-+            Reminder_Action::clearLastTriggered($issue_id);
-+
-+            // if old status was closed and new status is not, clear closed data from issue.
-+            if (@$old_details['sta_is_closed'] == 1) {
-+                $new_details = Status::getDetails($status_id);
-+                if ($new_details['sta_is_closed'] != 1) {
-+                    self::clearClosed($issue_id);
-+                }
-+            }
-+
-+            if ($notify) {
-+                Notification::notifyStatusChange($issue_id, $old_status, $status_id);
-+            }
-+
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remotely set the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing this change
-+     * @param   integer $new_status The new status ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setRemoteStatus($issue_id, $usr_id, $new_status)
-+    {
-+        $sta_id = Status::getStatusID($new_status);
-+
-+        $res = self::setStatus($issue_id, $sta_id);
-+        if ($res == 1) {
-+            // record history entry
-+            History::add($issue_id, $usr_id, History::getTypeID('remote_status_change'), "Status remotely changed to '$new_status' by " . User::getFullName($usr_id));
-+        }
-+        return $res;
-+    }
-+
-+
-+    /**
-+     * Method used to set the release of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $pre_id The ID of the release to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setRelease($issue_id, $pre_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $pre_id = Misc::escapeInteger($pre_id);
-+
-+        if ($pre_id != self::getRelease($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_pre_id = $pre_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current release of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The release
-+     */
-+    function getRelease($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_pre_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to set the priority of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $pri_id The ID of the priority to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setPriority($issue_id, $pri_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $pri_id = Misc::escapeInteger($pri_id);
-+
-+        if ($pri_id != self::getPriority($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_pri_id = $pri_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current issue priority
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The priority
-+     */
-+    function getPriority($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_pri_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to set the category of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $prc_id The ID of the category to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setCategory($issue_id, $prc_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $prc_id = Misc::escapeInteger($prc_id);
-+
-+        if ($prc_id != self::getPriority($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_prc_id = $prc_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current issue category
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The category
-+     */
-+    function getCategory($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_prc_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get all issues associated with a status that doesn't have
-+     * the 'closed' context.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID to list issues from
-+     * @param   integer $usr_id The user ID of the user requesting this information
-+     * @param   boolean $show_all_issues Whether to show all open issues, or just the ones assigned to the given email address
-+     * @param   integer $status_id The status ID to be used to restrict results
-+     * @return  array The list of open issues
-+     */
-+    function getOpenIssues($prj_id, $usr_id, $show_all_issues, $status_id)
-+    {
-+        $prj_id = Misc::escapeInteger($prj_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+        $projects = Project::getRemoteAssocListByUser($usr_id);
-+        if (@count($projects) == 0) {
-+            return '';
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary,
-+                    sta_title
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                    )
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id
-+                 WHERE ";
-+        if (!empty($status_id)) {
-+            $stmt .= " sta_id=$status_id AND ";
-+        }
-+        $stmt .= "
-+                    iss_prj_id=$prj_id AND
-+                    sta_id=iss_sta_id AND
-+                    sta_is_closed=0";
-+        if ($show_all_issues == false) {
-+            $stmt .= " AND
-+                    isu_usr_id=$usr_id";
-+        }
-+        $stmt .= "\nGROUP BY
-+                        iss_id";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            if (count($res) > 0) {
-+                self::getAssignedUsersByIssues($res);
-+            }
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to build the required parameters to simulate an email reply
-+     * to the user who reported the issue, using the issue details like summary
-+     * and description as email fields.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The email parameters
-+     */
-+    function getReplyDetails($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $stmt = "SELECT
-+                    iss_created_date,
-+                    usr_full_name AS reporter,
-+                    usr_email AS reporter_email,
-+                    iss_description AS description,
-+                    iss_summary AS sup_subject
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    iss_usr_id=usr_id AND
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $res['reply_subject'] = 'Re: [#' . $issue_id . '] ' . $res["sup_subject"];
-+            $res['created_date_ts'] = Date_Helper::getUnixTimestamp($res['iss_created_date'], 'GMT');
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to record the last updated timestamp for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   string $type The type of update that was made (optional)
-+     * @return  boolean
-+     */
-+    function markAsUpdated($issue_id, $type = false)
-+    {
-+        $public = array("staff response", "customer action", "file uploaded", "user response");
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "'\n";
-+        if ($type != false) {
-+            if (in_array($type, $public)) {
-+                $field = "iss_last_public_action_";
-+            } else {
-+                $field = "iss_last_internal_action_";
-+            }
-+            $stmt .= ",\n " . $field . "date = '" . Date_Helper::getCurrentDateGMT() . "',\n" .
-+                $field . "type  ='" . Misc::escapeString($type) . "'\n";
-+        }
-+        $stmt .= "WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            // update last response dates if this is a staff response
-+            if ($type == "staff response") {
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         SET
-+                            iss_last_response_date='" . Date_Helper::getCurrentDateGMT() . "'
-+                         WHERE
-+                            iss_id = " . Misc::escapeInteger($issue_id);
-+                DB_Helper::getInstance()->query($stmt);
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         SET
-+                            iss_first_response_date='" . Date_Helper::getCurrentDateGMT() . "'
-+                         WHERE
-+                            iss_first_response_date IS NULL AND
-+                            iss_id = " . Misc::escapeInteger($issue_id);
-+                DB_Helper::getInstance()->query($stmt);
-+            }
-+
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to check whether a given issue has duplicates
-+     * or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  boolean
-+     */
-+    function hasDuplicates($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(iss_id)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to update the duplicated issues for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function updateDuplicates($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $ids = self::getDuplicateList($issue_id);
-+        if ($ids == '') {
-+            return -1;
-+        }
-+        $ids = @array_keys($ids);
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_prc_id=" . Misc::escapeInteger($_POST["category"]) . ",";
-+        if (@$_POST["keep"] == "no") {
-+            $stmt .= "iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",";
-+        }
-+        $stmt .= "
-+                    iss_pri_id=" . Misc::escapeInteger($_POST["priority"]) . ",
-+                    iss_sta_id=" . Misc::escapeInteger($_POST["status"]) . ",
-+                    iss_res_id=" . Misc::escapeInteger($_POST["resolution"]) . "
-+                 WHERE
-+                    iss_id IN (" . implode(", ", $ids) . ")";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // record the change
-+            for ($i = 0; $i < count($ids); $i++) {
-+                History::add($ids[$i], Auth::getUserID(), History::getTypeID('duplicate_update'),
-+                    "The details for issue #$issue_id were updated by " . User::getFullName(Auth::getUserID()) . " and the changes propagated to the duplicated issues.");
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get a list of the duplicate issues for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of duplicates
-+     */
-+    function getDuplicateList($issue_id)
-+    {
-+        $res = self::getDuplicateDetailsList($issue_id);
-+        if (@count($res) == 0) {
-+            return '';
-+        } else {
-+            $list = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                $list[$res[$i]['issue_id']] = $res[$i]['title'];
-+            }
-+            return $list;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get a list of the duplicate issues (and their details)
-+     * for a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of duplicates
-+     */
-+    function getDuplicateDetailsList($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_id issue_id,
-+                    iss_summary title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_sta_id=sta_id AND
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to clear the duplicate status of an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function clearDuplicateStatus($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_duplicated_iss_id=NULL
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // record the change
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('duplicate_removed'), "Duplicate flag was reset by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to mark an issue as a duplicate of an existing one.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function markAsDuplicate($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (!self::exists($issue_id)) {
-+            return -1;
-+        }
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($_POST["duplicated_issue"]) . "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if (!empty($_POST["comments"])) {
-+                // add note with the comments of marking an issue as a duplicate of another one
-+                $_POST['title'] = 'Issue duplication comments';
-+                $_POST["note"] = $_POST["comments"];
-+                Note::insert(Auth::getUserID(), $issue_id);
-+            }
-+            // record the change
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('duplicate_added'),
-+                    "Issue marked as a duplicate of issue #" . $_POST["duplicated_issue"] . " by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    function isDuplicate($issue_id)
-+    {
-+        $sql = "SELECT
-+                    count(iss_id)
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id) . " AND
-+                    iss_duplicated_iss_id IS NULL";
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        }
-+        if ($res > 0) {
-+            return false;
-+        } else {
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get an associative array of user ID => user
-+     * status associated with a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of users
-+     */
-+    function getAssignedUsersStatus($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_id,
-+                    usr_status
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the summary associated with a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  string The issue summary
-+     */
-+    function getTitle($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the issue ID associated with a specific summary.
-+     *
-+     * @access  public
-+     * @param   string $summary The summary to look for
-+     * @return  integer The issue ID
-+     */
-+    function getIssueID($summary)
-+    {
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_summary='" . Misc::escapeString($summary) . "'";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            if (empty($res)) {
-+                return 0;
-+            } else {
-+                return $res;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to add a new anonymous based issue in the system.
-+     *
-+     * @access  public
-+     * @return  integer The new issue ID
-+     */
-+    function addAnonymousReport()
-+    {
-+        $options = Project::getAnonymousPostOptions($_POST["project"]);
-+        $initial_status = Project::getInitialStatus($_POST["project"]);
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 (
-+                    iss_prj_id,
-+                    iss_prc_id,
-+                    iss_pre_id,
-+                    iss_pri_id,
-+                    iss_usr_id,";
-+        if (!empty($initial_status)) {
-+            $stmt .= "iss_sta_id,";
-+        }
-+        $stmt .= "
-+                    iss_created_date,
-+                    iss_last_public_action_date,
-+                    iss_last_public_action_type,
-+                    iss_summary,
-+                    iss_description,
-+                    iss_root_message_id
-+                 ) VALUES (
-+                    " . Misc::escapeInteger($_POST["project"]) . ",
-+                    " . $options["category"] . ",
-+                    0,
-+                    " . $options["priority"] . ",
-+                    " . $options["reporter"] . ",";
-+        if (!empty($initial_status)) {
-+            $stmt .= "$initial_status,";
-+        }
-+        $stmt .= "
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    'created',
-+                    '" . Misc::escapeString($_POST["summary"]) . "',
-+                    '" . Misc::escapeString($_POST["description"]) . "',
-+                    '" . Misc::escapeString(Mail_Helper::generateMessageID()) . "'
-+                 )";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return $res;
-+        } else {
-+            $new_issue_id = DB_Helper::get_last_insert_id();
-+            // log the creation of the issue
-+            History::add($new_issue_id, APP_SYSTEM_USER_ID, History::getTypeID('issue_opened_anon'), 'Issue opened anonymously');
-+
-+            // now process any files being uploaded
-+            $found = 0;
-+            for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+                if (!@empty($_FILES["file"]["name"][$i])) {
-+                    $found = 1;
-+                    break;
-+                }
-+            }
-+            if ($found) {
-+                $attachment_id = Attachment::add($new_issue_id, $options["reporter"], 'files uploaded anonymously');
-+                for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+                    $filename = @$_FILES["file"]["name"][$i];
-+                    if (empty($filename)) {
-+                        continue;
-+                    }
-+                    $blob = file_get_contents($_FILES["file"]["tmp_name"][$i]);
-+                    if (!empty($blob)) {
-+                        Attachment::addFile($attachment_id, $filename, $_FILES["file"]["type"][$i], $blob);
-+                    }
-+                }
-+            }
-+            // need to process any custom fields ?
-+            if (@count($_POST["custom_fields"]) > 0) {
-+                foreach ($_POST["custom_fields"] as $fld_id => $value) {
-+                    Custom_Field::associateIssue($new_issue_id, $fld_id, $value);
-+                }
-+            }
-+
-+            // now add the user/issue association
-+            $assign = array();
-+            $users = @$options["users"];
-+            $actions = Notification::getDefaultActions($new_issue_id, false, 'anon_issue');
-+            for ($i = 0; $i < count($users); $i++) {
-+                Notification::subscribeUser(APP_SYSTEM_USER_ID, $new_issue_id, $users[$i], $actions);
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $new_issue_id, $users[$i]);
-+                $assign[] = $users[$i];
-+            }
-+
-+            Workflow::handleNewIssue(Misc::escapeInteger($_POST["project"]),  $new_issue_id, false, false);
-+
-+            // also notify any users that want to receive emails anytime a new issue is created
-+            Notification::notifyNewIssue($_POST['project'], $new_issue_id);
-+
-+            return $new_issue_id;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove all issues associated with a specific list of
-+     * projects.
-+     *
-+     * @access  public
-+     * @param   array $ids The list of projects to look for
-+     * @return  boolean
-+     */
-+    function removeByProjects($ids)
-+    {
-+        $items = @implode(", ", Misc::escapeInteger($ids));
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id IN ($items)";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if (count($res) > 0) {
-+                self::deleteAssociations($res);
-+                Attachment::removeByIssues($res);
-+                SCM::removeByIssues($res);
-+                Impact_Analysis::removeByIssues($res);
-+                self::deleteUserAssociations($res);
-+                Note::removeByIssues($res);
-+                Time_Tracking::removeByIssues($res);
-+                Notification::removeByIssues($res);
-+                Custom_Field::removeByIssues($res);
-+                Phone_Support::removeByIssues($res);
-+                History::removeByIssues($res);
-+                // now really delete the issues
-+                $items = implode(", ", $res);
-+                $stmt = "DELETE FROM
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         WHERE
-+                            iss_id IN ($items)";
-+                DB_Helper::getInstance()->query($stmt);
-+            }
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to close off an issue.
-+     *
-+     * @access  public
-+     * @param   integer $usr_id The user ID
-+     * @param   integer $issue_id The issue ID
-+     * @param   bool $send_notification Whether to send a notification about this action or not
-+     * @param   integer $resolution_id The resolution ID
-+     * @param   integer $status_id The status ID
-+     * @param   string $reason The reason for closing this issue
-+     * @param   string  $send_notification_to Who this notification should be sent too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function close($usr_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason, $send_notification_to = 'internal')
-+    {
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $resolution_id = Misc::escapeInteger($resolution_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='closed',
-+                    iss_closed_date='" . Date_Helper::getCurrentDateGMT() . "',\n";
-+        if (!empty($resolution_id)) {
-+            $stmt .= "iss_res_id=$resolution_id,\n";
-+        }
-+        $stmt .= "iss_sta_id=$status_id
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $prj_id = self::getProjectID($issue_id);
-+
-+            // record the change
-+            History::add($issue_id, $usr_id, History::getTypeID('issue_closed'), "Issue updated to status '" . Status::getStatusTitle($status_id) . "' by " . User::getFullName($usr_id));
-+
-+            if ($send_notification_to == 'all') {
-+
-+                $from = User::getFromHeader($usr_id);
-+                $message_id = User::getFromHeader($usr_id);
-+                $full_email = Support::buildFullHeaders($issue_id, $message_id, $from,
-+                    '', '', 'Issue closed comments', $reason, '');
-+
-+                $structure = Mime_Helper::decode($full_email, true, false);
-+
-+                $email = array(
-+                    'ema_id'        =>  Email_Account::getEmailAccount(self::getProjectID($issue_id)),
-+                    'issue_id'      =>  $issue_id,
-+                    'message_id'    =>  $message_id,
-+                    'date'          =>  Date_Helper::getCurrentDateGMT(),
-+                    'subject'       =>  'Issue closed comments',
-+                    'from'          =>  $from,
-+                    'has_attachment'=>  0,
-+                    'body'          =>  $reason,
-+                    'full_email'    =>  $full_email,
-+                    'headers'       =>  $structure->headers
-+                );
-+                Support::insertEmail($email, $structure, $sup_id, true);
-+                $ids = $sup_id;
-+            } else {
-+                // add note with the reason to close the issue
-+                $_POST['title'] = 'Issue closed comments';
-+                $_POST["note"] = $reason;
-+                Note::insert($usr_id, $issue_id, false, true, true, $send_notification);
-+                $ids = false;
-+            }
-+
-+            if ($send_notification) {
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    // send a special confirmation email when customer issues are closed
-+                    $stmt = "SELECT
-+                                iss_customer_contact_id
-+                             FROM
-+                                " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                             WHERE
-+                                iss_id=$issue_id";
-+                    $customer_contact_id = DB_Helper::getInstance()->getOne($stmt);
-+                    if (!empty($customer_contact_id)) {
-+                        Customer::notifyIssueClosed($prj_id, $issue_id, $customer_contact_id, $send_notification, $resolution_id, $status_id, $reason);
-+                    }
-+                }
-+                // send notifications for the issue being closed
-+                Notification::notify($issue_id, 'closed', $ids);
-+            }
-+            Workflow::handleIssueClosed($prj_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to update the details of a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 or -2 otherwise
-+     */
-+    function update($issue_id)
-+    {
-+        global $errors;
-+        $errors = array();
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $usr_id = Auth::getUserID();
-+        $prj_id = self::getProjectID($issue_id);
-+
-+        $workflow = Workflow::preIssueUpdated($prj_id, $issue_id, $usr_id, $_POST);
-+        if ($workflow !== true) {
-+            return $workflow;
-+        }
-+
-+        // get all of the 'current' information of this issue
-+        $current = self::getDetails($issue_id);
-+        // update the issue associations
-+        if (empty($_POST['associated_issues'])) {
-+            $associated_issues = array();
-+        } else {
-+            $associated_issues = explode(',', @$_POST['associated_issues']);
-+            // make sure all associated issues are valid (and in this project)
-+            for ($i = 0; $i < count($associated_issues); $i++) {
-+                if (!self::exists(trim($associated_issues[$i]), false)) {
-+                    $errors['Associated Issues'][] = 'Issue #' . $associated_issues[$i] . ' does not exist and was removed from the list of associated issues.';
-+                    unset($associated_issues[$i]);
-+                }
-+            }
-+        }
-+        $association_diff = Misc::arrayDiff($current['associated_issues'], $associated_issues);
-+        if (count($association_diff) > 0) {
-+            // go through the new assocations, if association already exists, skip it
-+            $associations_to_remove = $current['associated_issues'];
-+            if (count($associated_issues) > 0) {
-+                foreach ($associated_issues as $index => $associated_id) {
-+                    if (!in_array($associated_id, $current['associated_issues'])) {
-+                        self::addAssociation($issue_id, $associated_id, $usr_id);
-+                    } else {
-+                        // already assigned, remove this user from list of users to remove
-+                        unset($associations_to_remove[array_search($associated_id, $associations_to_remove)]);
-+                    }
-+                }
-+            }
-+            if (count($associations_to_remove) > 0) {
-+                foreach ($associations_to_remove as $associated_id) {
-+                    self::deleteAssociation($issue_id, $associated_id);
-+                }
-+            }
-+        }
-+        $assignments_changed = false;
-+        if (@$_POST["keep_assignments"] == "no") {
-+            // only change the issue-user associations if there really were any changes
-+            $old_assignees = array_merge($current['assigned_users'], $current['assigned_inactive_users']);
-+            if (!empty($_POST['assignments'])) {
-+                $new_assignees = @$_POST['assignments'];
-+            } else {
-+                $new_assignees = array();
-+            }
-+            $assignment_notifications = array();
-+
-+            // remove people from the assignment list, if appropriate
-+            foreach ($old_assignees as $assignee) {
-+                if (!in_array($assignee, $new_assignees)) {
-+                    self::deleteUserAssociation($issue_id, $assignee);
-+                    $assignments_changed = true;
-+                }
-+            }
-+            // add people to the assignment list, if appropriate
-+            foreach ($new_assignees as $assignee) {
-+                if (!in_array($assignee, $old_assignees)) {
-+                    self::addUserAssociation($usr_id, $issue_id, $assignee);
-+                    Notification::subscribeUser($usr_id, $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'issue_update'), TRUE);
-+                    $assignment_notifications[] = $assignee;
-+                    $assignments_changed = true;
-+                }
-+            }
-+            if (count($assignment_notifications) > 0) {
-+                Notification::notifyNewAssignment($assignment_notifications, $issue_id);
-+            }
-+        }
-+        if (empty($_POST["estimated_dev_time"])) {
-+            $_POST["estimated_dev_time"] = 0;
-+        }
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='updated',";
-+        if (!empty($_POST["category"])) {
-+            $stmt .= "iss_prc_id=" . Misc::escapeInteger($_POST["category"]) . ",";
-+        }
-+        if (@$_POST["keep"] == "no") {
-+            $stmt .= "iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",";
-+        }
-+        if (!empty($_POST['expected_resolution_date'])) {
-+            $stmt .= "iss_expected_resolution_date='" . Misc::escapeString($_POST['expected_resolution_date']) . "',";
-+        } else {
-+            $stmt .= "iss_expected_resolution_date=null,";
-+        }
-+        $stmt .= "
-+                    iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",
-+                    iss_pri_id=" . Misc::escapeInteger($_POST["priority"]) . ",
-+                    iss_sta_id=" . Misc::escapeInteger($_POST["status"]) . ",
-+                    iss_res_id=" . Misc::escapeInteger($_POST["resolution"]) . ",
-+                    iss_summary='" . Misc::escapeString($_POST["summary"]) . "',
-+                    iss_description='" . Misc::escapeString($_POST["description"]) . "',
-+                    iss_dev_time='" . Misc::escapeString($_POST["estimated_dev_time"]) . "',
-+                    iss_percent_complete= '" . Misc::escapeString($_POST["percent_complete"]) . "',
-+                    iss_trigger_reminders=" . Misc::escapeInteger($_POST["trigger_reminders"]) . ",
-+                    iss_grp_id ='" . Misc::escapeInteger($_POST["group"]) . "'";
-+        if (isset($_POST['private'])) {
-+            $stmt .= ",
-+                    iss_private = " . Misc::escapeInteger($_POST['private']);
-+        }
-+        $stmt .= "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // add change to the history (only for changes on specific fields?)
-+            $updated_fields = array();
-+            if ($current["iss_expected_resolution_date"] != $_POST['expected_resolution_date']) {
-+                $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $_POST['expected_resolution_date']);
-+            }
-+            if ($current["iss_prc_id"] != $_POST["category"]) {
-+                $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($_POST["category"]));
-+            }
-+            if ($current["iss_pre_id"] != $_POST["release"]) {
-+                $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($_POST["release"]));
-+            }
-+            if ($current["iss_pri_id"] != $_POST["priority"]) {
-+                $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($_POST["priority"]));
-+                Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $_POST);
-+            }
-+            if ($current["iss_sta_id"] != $_POST["status"]) {
-+                // clear out the last-triggered-reminder flag when changing the status of an issue
-+                Reminder_Action::clearLastTriggered($issue_id);
-+
-+                // if old status was closed and new status is not, clear closed data from issue.
-+                $old_status_details = Status::getDetails($current['iss_sta_id']);
-+                if ($old_status_details['sta_is_closed'] == 1) {
-+                    $new_status_details = Status::getDetails($_POST["status"]);
-+                    if ($new_status_details['sta_is_closed'] != 1) {
-+                        self::clearClosed($issue_id);
-+                    }
-+                }
-+                $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($_POST["status"]));
-+            }
-+            if ($current["iss_res_id"] != $_POST["resolution"]) {
-+                $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($_POST["resolution"]));
-+            }
-+            if ($current["iss_dev_time"] != $_POST["estimated_dev_time"]) {
-+                $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($_POST["estimated_dev_time"]*60)));
-+            }
-+            if ($current["iss_summary"] != $_POST["summary"]) {
-+                $updated_fields["Summary"] = '';
-+            }
-+            if ($current["iss_description"] != $_POST["description"]) {
-+                $updated_fields["Description"] = '';
-+            }
-+            if ((isset($_POST['private'])) && ($_POST['private'] != $current['iss_private'])) {
-+                $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($_POST['private']));
-+            }
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $i = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($i > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    if (($key != "Summary") && ($key != "Description")) {
-+                        $changes .= "$key: $value";
-+                    } else {
-+                        $changes .= "$key";
-+                    }
-+                    $i++;
-+                }
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
-+                // send notifications for the issue being updated
-+                Notification::notifyIssueUpdated($issue_id, $current, $_POST);
-+            }
-+
-+            // record group change as a seperate change
-+            if ($current["iss_grp_id"] != (int)$_POST["group"]) {
-+                History::add($issue_id, $usr_id, History::getTypeID('group_changed'),
-+                    "Group changed (" . History::formatChanges(Group::getName($current["iss_grp_id"]), Group::getName($_POST["group"])) . ") by " . User::getFullName($usr_id));
-+            }
-+
-+            // now update any duplicates, if any
-+            $update_dupe = array(
-+                'Category',
-+                'Release',
-+                'Priority',
-+                'Release',
-+                'Resolution'
-+            );
-+            // COMPAT: the following line requires PHP > 4.0.4
-+            $intersect = array_intersect($update_dupe, array_keys($updated_fields));
-+            if (($current["duplicates"] != '') && (count($intersect) > 0)) {
-+                self::updateDuplicates($issue_id);
-+            }
-+
-+            // if there is customer integration, mark last customer action
-+            if ((Customer::hasCustomerIntegration($prj_id)) && (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer'))) {
-+                self::recordLastCustomerAction($issue_id);
-+            }
-+
-+            if ($assignments_changed) {
-+                // XXX: we may want to also send the email notification for those "new" assignees
-+                Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, $usr_id, self::getDetails($issue_id), @$_POST['assignments'], false);
-+            }
-+
-+            Workflow::handleIssueUpdated($prj_id, $issue_id, $usr_id, $current, $_POST);
-+            // Move issue to another project
-+            if (isset($_POST['move_issue']) and (User::getRoleByUser($usr_id, $prj_id) >= User::getRoleID("Developer"))) {
-+                $new_prj_id = (int)@$_POST['new_prj'];
-+                if (($prj_id != $new_prj_id) && (array_key_exists($new_prj_id, Project::getAssocList($usr_id)))) {
-+                    if(User::getRoleByUser($usr_id, $new_prj_id) >= User::getRoleID("Reporter")) {
-+                        $res = self::moveIssue($issue_id, $new_prj_id);
-+                        if ($res == -1) {
-+                            return $res;
-+                        }
-+                    } else {
-+                        return -1;
-+                    }
-+                }
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Move the issue to a new project
-+     *
-+     * @param integer $issue_id
-+     * @param integer $new_prj_id
-+     * @return integer 1 on success, -1 otherwise
-+     */
-+    function moveIssue($issue_id, $new_prj_id)
-+    {
-+        $stmt = "UPDATE
-+              " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+          SET
-+              iss_prj_id = " . Misc::escapeInteger($new_prj_id) . "
-+          WHERE
-+              iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $currentDetails = self::getDetails($issue_id);
-+
-+            // set new category
-+            $new_iss_prc_list = Category::getAssocList($new_prj_id);
-+            $iss_prc_title = Category::getTitle($currentDetails['iss_prc_id']);
-+            $new_prc_id = array_search($iss_prc_title, $new_iss_prc_list);
-+            if ($new_prc_id === false) {
-+              // use the first category listed in the new project
-+              $new_prc_id = key($new_iss_prc_list);
-+            }
-+
-+            // set new priority
-+            $new_iss_pri_list = Priority::getAssocList($new_prj_id);
-+            $iss_pri_title = Priority::getTitle($currentDetails['iss_pri_id']);
-+            $new_pri_id = array_search($iss_pri_title, $new_iss_pri_list);
-+            if ($new_pri_id === false) {
-+              // use the first category listed in the new project
-+              $new_pri_id = key($new_iss_pri_list);
-+            }
-+
-+            // XXX: Set status if needed when moving issue
-+
-+            $stmt = "UPDATE
-+                  " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+              SET
-+                  iss_prc_id=" . Misc::escapeInteger($new_prc_id) . ",
-+                  iss_pri_id=" . $new_pri_id . "
-+              WHERE
-+                  iss_id=$issue_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            }
-+
-+            // clear project cache
-+            self::getProjectID($issue_id, true);
-+
-+            Notification::notifyNewIssue($new_prj_id, $issue_id);
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to associate an existing issue with another one.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $issue_id The other issue ID
-+     * @return  void
-+     */
-+    function addAssociation($issue_id, $associated_id, $usr_id, $link_issues = TRUE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $associated_id = Misc::escapeInteger($associated_id);
-+
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 (
-+                    isa_issue_id,
-+                    isa_associated_id
-+                 ) VALUES (
-+                    $issue_id,
-+                    $associated_id
-+                 )";
-+        DB_Helper::getInstance()->query($stmt);
-+        History::add($issue_id, $usr_id, History::getTypeID('issue_associated'), "Issue associated to #$associated_id by " . User::getFullName($usr_id));
-+        // link the associated issue back to this one
-+        if ($link_issues) {
-+            self::addAssociation($associated_id, $issue_id, $usr_id, FALSE);
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove the issue associations related to a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  void
-+     */
-+    function deleteAssociations($issue_id, $usr_id = FALSE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (is_array($issue_id)) {
-+            $issue_id = implode(", ", $issue_id);
-+        }
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 WHERE
-+                    isa_issue_id IN ($issue_id) OR
-+                    isa_associated_id IN ($issue_id)";
-+        DB_Helper::getInstance()->query($stmt);
-+        if ($usr_id) {
-+            History::add($issue_id, $usr_id, History::getTypeID('issue_all_unassociated'), 'Issue associations removed by ' . User::getFullName($usr_id));
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove a issue association from an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $associated_id The associated issue ID to remove.
-+     * @return  void
-+     */
-+    function deleteAssociation($issue_id, $associated_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $associated_id = Misc::escapeInteger($associated_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 WHERE
-+                    (
-+                        isa_issue_id = $issue_id AND
-+                        isa_associated_id = $associated_id
-+                    ) OR
-+                    (
-+                        isa_issue_id = $associated_id AND
-+                        isa_associated_id = $issue_id
-+                    )";
-+        DB_Helper::getInstance()->query($stmt);
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_unassociated'),
-+                "Issue association #$associated_id removed by " . User::getFullName(Auth::getUserID()));
-+        History::add($associated_id, Auth::getUserID(), History::getTypeID('issue_unassociated'),
-+                "Issue association #$issue_id removed by " . User::getFullName(Auth::getUserID()));
-+    }
-+
-+
-+    /**
-+     * Method used to assign an issue with an user.
-+     *
-+     * @access  public
-+     * @param   integer $usr_id The user ID of the person performing this change
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $assignee_usr_id The user ID of the assignee
-+     * @param   boolean $add_history Whether to add a history entry about this or not
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function addUserAssociation($usr_id, $issue_id, $assignee_usr_id, $add_history = TRUE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $assignee_usr_id = Misc::escapeInteger($assignee_usr_id);
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 (
-+                    isu_iss_id,
-+                    isu_usr_id,
-+                    isu_assigned_date
-+                 ) VALUES (
-+                    $issue_id,
-+                    $assignee_usr_id,
-+                    '" . Date_Helper::getCurrentDateGMT() . "'
-+                 )";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($add_history) {
-+                History::add($issue_id, $usr_id, History::getTypeID('user_associated'),
-+                    'Issue assigned to ' . User::getFullName($assignee_usr_id) . ' by ' . User::getFullName($usr_id));
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to delete all user assignments for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing the change
-+     * @return  void
-+     */
-+    function deleteUserAssociations($issue_id, $usr_id = FALSE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (is_array($issue_id)) {
-+            $issue_id = implode(", ", $issue_id);
-+        }
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id IN ($issue_id)";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($usr_id) {
-+                History::add($issue_id, $usr_id, History::getTypeID('user_all_unassociated'), 'Issue assignments removed by ' . User::getFullName($usr_id));
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to delete a single user assignments for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user to remove.
-+     * @param   boolean $add_history Whether to add a history entry about this or not
-+     * @return  void
-+     */
-+    function deleteUserAssociation($issue_id, $usr_id, $add_history = true)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id = $issue_id AND
-+                    isu_usr_id = $usr_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($add_history) {
-+                History::add($issue_id, Auth::getUserID(), History::getTypeID('user_unassociated'),
-+                    User::getFullName($usr_id) . ' removed from issue by ' . User::getFullName(Auth::getUserID()));
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Creates an issue with the given email information.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @param   integer $usr_id The user responsible for this action
-+     * @param   string $sender The original sender of this email
-+     * @param   string $summary The issue summary
-+     * @param   string $description The issue description
-+     * @param   integer $category The category ID
-+     * @param   integer $priority The priority ID
-+     * @param   array $assignment The list of users to assign this issue to
-+     * @param   string $date The date the email was originally sent.
-+     * @param   string $msg_id The message ID of the email we are creating this issue from.
-+     * @return  void
-+     */
-+    function createFromEmail($prj_id, $usr_id, $sender, $summary, $description, $category, $priority, $assignment, $date, $msg_id)
-+    {
-+        $data = array();
-+        $exclude_list = array();
-+
-+        $sender_email = Mail_Helper::getEmailAddress($sender);
-+        $sender_usr_id = User::getUserIDByEmail($sender_email, true);
-+        if (!empty($sender_usr_id)) {
-+            $reporter = $sender_usr_id;
-+            $exclude_list[] = $sender_usr_id;
-+        }
-+
-+        $data = array(
-+            'category' => $category,
-+            'priority' => $priority,
-+            'description' => $description,
-+            'summary' => $summary,
-+            'msg_id' => $msg_id,
-+        );
-+
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            list($customer_id, $customer_contact_id) = Customer::getCustomerIDByEmails($prj_id, array($sender_email));
-+            if (!empty($customer_id)) {
-+                $contact = Customer::getContactDetails($prj_id, $customer_contact_id);
-+                // overwrite the reporter with the customer contact
-+                $reporter = User::getUserIDByContactID($customer_contact_id);
-+                $contact_timezone = Date_Helper::getPreferredTimezone($reporter);
-+
-+                $data['customer'] = $customer_id;
-+                $data['contact'] = $customer_contact_id;
-+#                $data['contract'] =  // XXX missing
-+                $data['contact_person_lname'] = $contact['last_name'];
-+                $data['contact_person_fname'] = $contact['first_name'];
-+                $data['contact_email'] = $sender_email;
-+                $data['contact_phone'] = $contact['phone'];
-+                $data['contact_timezone'] = $contact_timezone;
-+            }
-+        } else {
-+            $customer_id = FALSE;
-+        }
-+        if (empty($reporter)) {
-+            $reporter = APP_SYSTEM_USER_ID;
-+        }
-+
-+        $data['reporter'] = $reporter;
-+
-+        $issue_id = self::insertIssue($prj_id, $usr_id, $data);
-+        if ($issue_id == -1) {
-+            return -1;
-+        }
-+
-+        $has_TAM = false;
-+        $has_RR = false;
-+        // log the creation of the issue
-+        History::add($issue_id, $usr_id, History::getTypeID('issue_opened'), 'Issue opened by ' . $sender);
-+
-+        $emails = array();
-+        $manager_usr_ids = array();
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (!empty($customer_id))) {
-+            // if there are any technical account managers associated with this customer, add these users to the notification list
-+            $managers = Customer::getAccountManagers($prj_id, $customer_id);
-+            $manager_usr_ids = array_keys($managers);
-+            $manager_emails = array_values($managers);
-+            $emails = array_merge($emails, $manager_emails);
-+        }
-+        // add the reporter to the notification list
-+        $emails[] = $sender;
-+        $emails = array_unique($emails);
-+        $actions = Notification::getDefaultActions($issue_id, false, 'issue_from_email');
-+        foreach ($emails as $address) {
-+            Notification::subscribeEmail($reporter, $issue_id, $address, $actions);
-+        }
-+
-+        // only assign the issue to an user if the associated customer has any technical account managers
-+        $users = array();
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (count($manager_usr_ids) > 0)) {
-+            foreach ($manager_usr_ids as $manager_usr_id) {
-+                $users[] = $manager_usr_id;
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $manager_usr_id, false);
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)');
-+            }
-+            $has_TAM = true;
-+        }
-+        // now add the user/issue association
-+        if (@count($assignment) > 0) {
-+            for ($i = 0; $i < count($assignment); $i++) {
-+                Notification::subscribeUser($reporter, $issue_id, $assignment[$i], $actions);
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $assignment[$i]);
-+                if ($assignment[$i] != $usr_id) {
-+                    $users[] = $assignment[$i];
-+                }
-+            }
-+        } else {
-+            // only use the round-robin feature if this new issue was not
-+            // already assigned to a customer account manager
-+            if (@count($manager_usr_ids) < 1) {
-+                $assignee = Round_Robin::getNextAssignee($prj_id);
-+                // assign the issue to the round robin person
-+                if (!empty($assignee)) {
-+                    self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $assignee, false);
-+                    History::add($issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)');
-+                    $users[] = $assignee;
-+                    $has_RR = true;
-+                }
-+            }
-+        }
-+        if (count($users) > 0) {
-+            $has_assignee = true;
-+        }
-+
-+        Workflow::handleNewIssue($prj_id, $issue_id, $has_TAM, $has_RR);
-+
-+        // send special 'an issue was auto-created for you' notification back to the sender
-+        Notification::notifyAutoCreatedIssue($prj_id, $issue_id, $sender, $date, $summary);
-+
-+        // also notify any users that want to receive emails anytime a new issue is created
-+        Notification::notifyNewIssue($prj_id, $issue_id, $exclude_list);
-+
-+        return $issue_id;
-+    }
-+
-+
-+    /**
-+     * Return errors that happened when creating new issue from POST method.
-+     *
-+     * @return  array
-+     */
-+    private static $insert_errors = array();
-+    static function getInsertErrors() {
-+        return self::$insert_errors;
-+    }
-+
-+    /**
-+     * Method used to add a new issue using the normal report form.
-+     *
-+     * @access  public
-+     * @return  integer The new issue ID
-+     */
-+    function createFromPost()
-+    {
-+        $keys = array(
-+            'add_primary_contact', 'attached_emails', 'category', 'contact', 'contact_email', 'contact_extra_emails', 'contact_person_fname',
-+            'contact_person_lname', 'contact_phone', 'contact_timezone', 'contract', 'customer', 'custom_fields', 'description',
-+            'estimated_dev_time', 'group', 'notify_customer', 'notify_senders', 'priority', 'private', 'release', 'summary', 'users',
-+        );
-+        $data = array();
-+        foreach ($keys as $key) {
-+            if (isset($_POST[$key])) {
-+                $data[$key] = $_POST[$key];
-+            }
-+        }
-+
-+        $prj_id = Auth::getCurrentProject();
-+        $usr_id = Auth::getUserID();
-+
-+        // if we are creating an issue for a customer, put the
-+        // main customer contact as the reporter for it
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $contact_usr_id = User::getUserIDByContactID($data['contact']);
-+            if (empty($contact_usr_id)) {
-+                $contact_usr_id = $usr_id;
-+            }
-+            $data['reporter'] = $contact_usr_id;
-+        } else {
-+            $data['reporter'] = $usr_id;
-+        }
-+
-+        $data['msg_id'] = Mail_Helper::generateMessageID();
-+
-+        $issue_id = self::insertIssue($prj_id, $usr_id, $data);
-+        if ($issue_id == -1) {
-+            return -1;
-+        }
-+
-+        $has_TAM = false;
-+        $has_RR = false;
-+        $info = User::getNameEmail($usr_id);
-+        // log the creation of the issue
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_opened'), 'Issue opened by ' . User::getFullName(Auth::getUserID()));
-+
-+        $emails = array();
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            if (!empty($data['contact_extra_emails']) && count($data['contact_extra_emails']) > 0) {
-+                $emails = $data['contact_extra_emails'];
-+            }
-+            // add the primary contact to the notification list
-+            if ($data['add_primary_contact'] == 'yes') {
-+                $contact_email = User::getEmailByContactID($data['contact']);
-+                if (!empty($contact_email)) {
-+                    $emails[] = $contact_email;
-+                }
-+            }
-+            // if there are any technical account managers associated with this customer, add these users to the notification list
-+            $managers = Customer::getAccountManagers($prj_id, $data['customer']);
-+            $manager_usr_ids = array_keys($managers);
-+            $manager_emails = array_values($managers);
-+            $emails = array_merge($emails, $manager_emails);
-+        }
-+        // add the reporter to the notification list
-+        $emails[] = $info['usr_email'];
-+        $emails = array_unique($emails);
-+        foreach ($emails as $address) {
-+            Notification::subscribeEmail($usr_id, $issue_id, $address, Notification::getDefaultActions($issue_id, $address, 'new_issue'));
-+        }
-+
-+        // only assign the issue to an user if the associated customer has any technical account managers
-+        $users = array();
-+        $has_TAM = false;
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (count($manager_usr_ids) > 0)) {
-+            foreach ($manager_usr_ids as $manager_usr_id) {
-+                $users[] = $manager_usr_id;
-+                self::addUserAssociation($usr_id, $issue_id, $manager_usr_id, false);
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)');
-+            }
-+            $has_TAM = true;
-+        }
-+        // now add the user/issue association (aka assignments)
-+        if (!empty($data['users']) && count($data['users']) > 0) {
-+            for ($i = 0; $i < count($data['users']); $i++) {
-+                Notification::subscribeUser($usr_id, $issue_id, $data['users'][$i],
-+                                Notification::getDefaultActions($issue_id, User::getEmail($data['users'][$i]), 'new_issue'));
-+                self::addUserAssociation($usr_id, $issue_id, $data['users'][$i]);
-+                if ($data['users'][$i] != $usr_id) {
-+                    $users[] = $data['users'][$i];
-+                }
-+            }
-+        } else {
-+            // only use the round-robin feature if this new issue was not
-+            // already assigned to a customer account manager
-+            if (@count($manager_usr_ids) < 1) {
-+                $assignee = Round_Robin::getNextAssignee($prj_id);
-+                // assign the issue to the round robin person
-+                if (!empty($assignee)) {
-+                    $users[] = $assignee;
-+                    self::addUserAssociation($usr_id, $issue_id, $assignee, false);
-+                    History::add($issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)');
-+                    $has_RR = true;
-+                }
-+            }
-+        }
-+
-+        // now process any files being uploaded
-+        $found = 0;
-+        for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+            if (!@empty($_FILES["file"]["name"][$i])) {
-+                $found = 1;
-+                break;
-+            }
-+        }
-+        if ($found) {
-+            $files = array();
-+            for ($i = 0; $i < count($_FILES["file"]["name"]); $i++) {
-+                $filename = @$_FILES["file"]["name"][$i];
-+                if (empty($filename)) {
-+                    continue;
-+                }
-+                $blob = file_get_contents($_FILES["file"]["tmp_name"][$i]);
-+                if (empty($blob)) {
-+                    // error reading a file
-+                    self::$insert_errors["file[$i]"] = "There was an error uploading the file '$filename'.";
-+                    continue;
-+                }
-+                $files[] = array(
-+                    "filename" => $filename,
-+                    "type"     => $_FILES['file']['type'][$i],
-+                    "blob"     => $blob
-+                );
-+            }
-+            if (count($files) > 0) {
-+                $attachment_id = Attachment::add($issue_id, $usr_id, 'Files uploaded at issue creation time');
-+                foreach ($files as $file) {
-+                    Attachment::addFile($attachment_id, $file["filename"], $file["type"], $file["blob"]);
-+                }
-+            }
-+        }
-+        // need to associate any emails ?
-+        if (!empty($data['attached_emails'])) {
-+            $items = explode(",", $data['attached_emails']);
-+            Support::associate($usr_id, $issue_id, $items);
-+        }
-+        // need to notify any emails being converted into issues ?
-+        if (@count($data['notify_senders']) > 0) {
-+            $recipients = Notification::notifyEmailConvertedIntoIssue($prj_id, $issue_id, $data['notify_senders'], @$data['customer']);
-+        } else {
-+            $recipients = array();
-+        }
-+        // need to process any custom fields ?
-+        if (@count($data['custom_fields']) > 0) {
-+            foreach ($data['custom_fields'] as $fld_id => $value) {
-+                Custom_Field::associateIssue($issue_id, $fld_id, $value);
-+            }
-+        }
-+        // also send a special confirmation email to the customer contact
-+        if ((@$data['notify_customer'] == 'yes') && (!empty($data['contact']))) {
-+            // also need to pass the list of sender emails already notified,
-+            // so we can avoid notifying the same person again
-+            $contact_email = User::getEmailByContactID($data['contact']);
-+            if (@!in_array($contact_email, $recipients)) {
-+                Customer::notifyCustomerIssue($prj_id, $issue_id, $data['contact']);
-+            }
-+        }
-+
-+        Workflow::handleNewIssue($prj_id, $issue_id, $has_TAM, $has_RR);
-+
-+        // also notify any users that want to receive emails anytime a new issue is created
-+        Notification::notifyNewIssue($prj_id, $issue_id);
-+
-+        return $issue_id;
-+    }
-+
-+    /**
-+     * Insert issue to database.
-+     *
-+     * @param   integer $prj_id The project ID
-+     * @param   integer $usr_id The user responsible for this action
-+     * @param   array $data of issue to be inserted
-+     * @return  integer The new issue ID
-+     */
-+    private function insertIssue($prj_id, $usr_id, $data)
-+    {
-+
-+        // XXX missing_fields never used
-+        $missing_fields = array();
-+        if ($data['category'] == -1) {
-+            $missing_fields[] = 'Category';
-+        }
-+        if ($data['priority'] == -1) {
-+            $missing_fields[] = 'Priority';
-+        }
-+
-+        // if there is no reporter set, use the system user
-+        if (empty($data['reporter'])) {
-+              $data['reporter'] = APP_SYSTEM_USER_ID;
-+        }
-+
-+        if ((!isset($data['estimated_dev_time'])) || ($data['estimated_dev_time'] == '')) {
-+            $data['estimated_dev_time'] = 0;
-+        }
-+
-+        // add new issue
-+        $stmt = "INSERT INTO " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue ".
-+                "SET ".
-+                    "iss_prj_id=" . $prj_id . ",";
-+        if (!empty($data['group'])) {
-+            $stmt .= "iss_grp_id=" . Misc::escapeInteger($data['group']) . ",\n";
-+        }
-+        if (!empty($data['category'])) {
-+            $stmt .= "iss_prc_id=". Misc::escapeInteger($data['category']) . ",\n";
-+        }
-+        if (!empty($data['release'])) {
-+            $stmt .= "iss_pre_id=". Misc::escapeInteger($data['release']) . ",\n";
-+        }
-+        if (!empty($data['priority'])) {
-+            $stmt .= "iss_pri_id=". Misc::escapeInteger($data['priority']) . ",";
-+        }
-+
-+        $stmt .= "iss_usr_id=". Misc::escapeInteger($data['reporter']) .",";
-+
-+        $initial_status = Project::getInitialStatus($prj_id);
-+        if (!empty($initial_status)) {
-+            $stmt .= "iss_sta_id=" . Misc::escapeInteger($initial_status) . ",";
-+        }
-+
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $stmt .= "
-+                    iss_customer_id=". Misc::escapeInteger($data['customer']) . ",";
-+            if (!empty($data['contact'])) {
-+            $stmt .= "
-+                    iss_customer_contract_id='". Misc::escapeString($data['contract']) . "',";
-+            }
-+            $stmt .= "
-+                    iss_customer_contact_id=". Misc::escapeInteger($data['contact']) . ",
-+                    iss_contact_person_lname='". Misc::escapeString($data['contact_person_lname']) . "',
-+                    iss_contact_person_fname='". Misc::escapeString($data['contact_person_fname']) . "',
-+                    iss_contact_email='". Misc::escapeString($data['contact_email']) . "',
-+                    iss_contact_phone='". Misc::escapeString($data['contact_phone']) . "',
-+                    iss_contact_timezone='". Misc::escapeString($data['contact_timezone']) . "',";
-+        }
-+
-+        $stmt .= "
-+                    iss_created_date='". Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='created',
-+                    iss_summary='" . Misc::escapeString($data['summary']) . "',
-+                    iss_description='" . Misc::escapeString($data['description']) . "',
-+                    iss_dev_time='" . Misc::escapeString($data['estimated_dev_time']) . "',";
-+            if (!empty($data['contact'])) {
-+                $stmt .= "
-+                    iss_private=" . Misc::escapeInteger($data['private']) . " ,";
-+            }
-+        $stmt .= "
-+                    iss_root_message_id='". Misc::escapeString($data['msg_id']) ."'
-+        ";
-+
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+
-+        $issue_id = DB_Helper::get_last_insert_id();
-+        return $issue_id;
-+    }
-+
-+
-+    /**
-+     * Method used to get a specific parameter in the issue listing cookie.
-+     *
-+     * @access  public
-+     * @param   string $name The name of the parameter
-+     * @return  mixed The value of the specified parameter
-+     */
-+    function getParam($name)
-+    {
-+        $profile = Search_Profile::getProfile(Auth::getUserID(), Auth::getCurrentProject(), 'issue');
-+
-+        if (isset($_GET[$name])) {
-+            return $_GET[$name];
-+        } elseif (isset($_POST[$name])) {
-+            return $_POST[$name];
-+        } elseif (isset($profile[$name])) {
-+            return $profile[$name];
-+        } else {
-+            return "";
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to save the current search parameters in a cookie.
-+     *
-+     * @access  public
-+     * @return  array The search parameters
-+     */
-+    function saveSearchParams()
-+    {
-+        $sort_by = self::getParam('sort_by');
-+        $sort_order = self::getParam('sort_order');
-+        $rows = self::getParam('rows');
-+        $hide_closed = self::getParam('hide_closed');
-+        if ($hide_closed === '') {
-+            $hide_closed = 1;
-+        }
-+        $search_type = self::getParam('search_type');
-+        if (empty($search_type)) {
-+            $search_type = 'all_text';
-+        }
-+        $custom_field = self::getParam('custom_field');
-+        if (is_string($custom_field)) {
-+            $custom_field = unserialize(urldecode($custom_field));
-+        }
-+        $cookie = array(
-+            'rows'           => $rows ? $rows : APP_DEFAULT_PAGER_SIZE,
-+            'pagerRow'       => self::getParam('pagerRow'),
-+            'hide_closed'    => $hide_closed,
-+            "sort_by"        => $sort_by ? $sort_by : "pri_rank",
-+            "sort_order"     => $sort_order ? $sort_order : "ASC",
-+            // quick filter form
-+            'keywords'       => self::getParam('keywords'),
-+            'search_type'    => $search_type,
-+            'users'          => self::getParam('users'),
-+            'status'         => self::getParam('status'),
-+            'priority'       => self::getParam('priority'),
-+            'category'       => self::getParam('category'),
-+            'customer_email' => self::getParam('customer_email'),
-+            // advanced search form
-+            'show_authorized_issues'        => self::getParam('show_authorized_issues'),
-+            'show_notification_list_issues' => self::getParam('show_notification_list_issues'),
-+            'reporter'       => self::getParam('reporter'),
-+            // other fields
-+            'release'        => self::getParam('release'),
-+            // custom fields
-+            'custom_field'   => $custom_field
-+        );
-+        // now do some magic to properly format the date fields
-+        $date_fields = array(
-+            'created_date',
-+            'updated_date',
-+            'last_response_date',
-+            'first_response_date',
-+            'closed_date'
-+        );
-+        foreach ($date_fields as $field_name) {
-+            $field = self::getParam($field_name);
-+            if (empty($field)) {
-+                continue;
-+            }
-+            if (@$field['filter_type'] == 'in_past') {
-+                @$cookie[$field_name] = array(
-+                    'filter_type'   =>  'in_past',
-+                    'time_period'   =>  $field['time_period']
-+                );
-+            } else {
-+                $end_field_name = $field_name . '_end';
-+                $end_field = self::getParam($end_field_name);
-+                @$cookie[$field_name] = array(
-+                    'past_hour'   => $field['past_hour'],
-+                    'Year'        => $field['Year'],
-+                    'Month'       => $field['Month'],
-+                    'Day'         => $field['Day'],
-+                    'start'       => $field['Year'] . '-' . $field['Month'] . '-' . $field['Day'],
-+                    'filter_type' => $field['filter_type'],
-+                    'end'         => $end_field['Year'] . '-' . $end_field['Month'] . '-' . $end_field['Day']
-+                );
-+                @$cookie[$end_field_name] = array(
-+                    'Year'        => $end_field['Year'],
-+                    'Month'       => $end_field['Month'],
-+                    'Day'         => $end_field['Day']
-+                );
-+            }
-+        }
-+        Search_Profile::save(Auth::getUserID(), Auth::getCurrentProject(), 'issue', $cookie);
-+        return $cookie;
-+    }
-+
-+
-+    /**
-+     * Method used to get the current sorting options used in the grid layout
-+     * of the issue listing page.
-+     *
-+     * @access  public
-+     * @param   array $options The current search parameters
-+     * @return  array The sorting options
-+     */
-+    function getSortingInfo($options)
-+    {
-+
-+        $custom_fields = Custom_Field::getFieldsToBeListed(Auth::getCurrentProject());
-+
-+        // default order for last action date, priority should be descending
-+        // for textual fields, like summary, ascending is reasonable
-+        $fields = array(
-+            "pri_rank" => "desc",
-+            "iss_id" => "desc",
-+            "iss_customer_id" => "desc",
-+            "prc_title" => "asc",
-+            "sta_rank" => "asc",
-+            "iss_created_date" => "desc",
-+            "iss_summary" => "asc",
-+            "last_action_date" => "desc",
-+            "usr_full_name" => "asc",
-+            "iss_expected_resolution_date" => "desc",
-+            "pre_title" => "asc",
-+            "assigned" => "asc",
-+        );
-+
-+        foreach ($custom_fields as $fld_id => $fld_name) {
-+            $fields['custom_field_' . $fld_id] = "desc";
-+        }
-+
-+        $sortfields = array_combine(array_keys($fields), array_keys($fields));
-+        $sortfields["pre_title"] = "pre_scheduled_date";
-+        $sortfields["assigned"] = "isu_usr_id";
-+
-+        $items = array(
-+            "links"  => array(),
-+            "images" => array()
-+        );
-+        foreach ($sortfields as $field => $sortfield) {
-+            $sort_order = $fields[$field];
-+            if ($options["sort_by"] == $sortfield) {
-+                $items["images"][$field] = "images/" . strtolower($options["sort_order"]) . ".gif";
-+                if (strtolower($options["sort_order"]) == "asc") {
-+                    $sort_order = "desc";
-+                } else {
-+                    $sort_order = "asc";
-+                }
-+            }
-+            $items["links"][$field] = $_SERVER["PHP_SELF"] . "?sort_by=" . $sortfield . "&sort_order=" . $sort_order;
-+        }
-+        return $items;
-+    }
-+
-+
-+    /**
-+     * Returns the list of action date fields appropriate for the
-+     * current user ID.
-+     *
-+     * @access  public
-+     * @return  array The list of action date fields
-+     */
-+    function getLastActionFields()
-+    {
-+        $last_action_fields = array(
-+            "iss_last_public_action_date"
-+        );
-+        if (Auth::getCurrentRole() > User::getRoleID('Customer')) {
-+            $last_action_fields[] = "iss_last_internal_action_date";
-+        }
-+        if (count($last_action_fields) > 1) {
-+            return "GREATEST(" . implode(', IFNULL(', $last_action_fields) . ", '0000-00-00')) AS last_action_date";
-+        } else {
-+            return $last_action_fields[0] . " AS last_action_date";
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues to be displayed in the grid layout.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The current project ID
-+     * @param   array $options The search parameters
-+     * @param   integer $current_row The current page number
-+     * @param   integer $max The maximum number of rows per page
-+     * @return  array The list of issues to be displayed
-+     */
-+    function getListing($prj_id, $options, $current_row = 0, $max = 5)
-+    {
-+        if (strtoupper($max) == "ALL") {
-+            $max = 9999999;
-+        }
-+        $start = $current_row * $max;
-+        // get the current user's role
-+        $usr_id = Auth::getUserID();
-+        $role_id = User::getRoleByUser($usr_id, $prj_id);
-+
-+        // get any custom fields that should be displayed
-+        $custom_fields = Custom_Field::getFieldsToBeListed($prj_id);
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_grp_id,
-+                    iss_prj_id,
-+                    iss_sta_id,
-+                    iss_customer_id,
-+                    iss_customer_contract_id,
-+                    iss_created_date,
-+                    iss_updated_date,
-+                    iss_last_response_date,
-+                    iss_closed_date,
-+                    iss_last_customer_action_date,
-+                    iss_usr_id,
-+                    iss_summary,
-+                    pri_title,
-+                    prc_title,
-+                    sta_title,
-+                    sta_color status_color,
-+                    sta_id,
-+                    iqu_status,
-+                    grp_name `group`,
-+                    pre_title,
-+                    iss_last_public_action_date,
-+                    iss_last_public_action_type,
-+                    iss_last_internal_action_date,
-+                    iss_last_internal_action_type,
-+                    " . self::getLastActionFields() . ",
-+                    IF(iss_last_internal_action_date > iss_last_public_action_date, 'internal', 'public') AS action_type,
-+                    iss_private,
-+                    usr_full_name,
-+                    iss_percent_complete,
-+                    iss_dev_time,
-+                    iss_expected_resolution_date
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user";
-+        // join custom fields if we are searching by custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                if (($field['fld_type'] == 'date') && ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . '_' . $cfo_id . "\n";
-+                    }
-+                } else {
-+                    $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . "\n";
-+                }
-+            }
-+        }
-+        $stmt .= ")";
-+        // check for the custom fields we want to sort by
-+        if (strstr($options['sort_by'], 'custom_field') !== false) {
-+            $fld_id = str_replace("custom_field_", '', $options['sort_by']);
-+            $stmt .= "\n LEFT JOIN \n" .
-+                APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf_sort
-+                ON
-+                    (cf_sort.icf_iss_id = iss_id AND cf_sort.icf_fld_id = $fld_id) \n";
-+        }
-+        if (!empty($options["users"]) || $options["sort_by"] === "isu_usr_id") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id";
-+        }
-+        if ((!empty($options["show_authorized_issues"])) || (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id)))) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier
-+                 ON
-+                    iur_iss_id=iss_id";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
-+                 ON
-+                    sub_iss_id=iss_id";
-+        }
-+        $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . ".`" . APP_TABLE_PREFIX . "group`
-+                 ON
-+                    iss_grp_id=grp_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id=prc_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id = pre_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 ON
-+                    iss_id=iqu_iss_id AND
-+                    (iqu_expiration > '" . Date_Helper::getCurrentDateGMT() . "' OR iqu_expiration IS NULL)
-+                 WHERE
-+                    iss_prj_id= " . Misc::escapeInteger($prj_id);
-+        $stmt .= self::buildWhereClause($options);
-+
-+        if (strstr($options["sort_by"], 'custom_field') !== false) {
-+            $fld_details = Custom_Field::getDetails($fld_id);
-+            $sort_by = 'cf_sort.' . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']);
-+        } else {
-+            $sort_by = Misc::escapeString($options["sort_by"]);
-+        }
-+
-+        $stmt .= "
-+                 GROUP BY
-+                    iss_id
-+                 ORDER BY
-+                    " . $sort_by . " " . Misc::escapeString($options["sort_order"]) . ",
-+                    iss_id DESC";
-+        $total_rows = Pager::getTotalRows($stmt);
-+        $stmt .= "
-+                 LIMIT
-+                    " . Misc::escapeInteger($start) . ", " . Misc::escapeInteger($max);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array(
-+                "list" => "",
-+                "info" => ""
-+            );
-+        } else {
-+            if (count($res) > 0) {
-+                self::getAssignedUsersByIssues($res);
-+                Time_Tracking::getTimeSpentByIssues($res);
-+                // need to get the customer titles for all of these issues...
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    Customer::getCustomerTitlesByIssues($prj_id, $res);
-+                    Customer::getSupportLevelsByIssues($prj_id, $res);
-+                }
-+                self::formatLastActionDates($res);
-+                self::getLastStatusChangeDates($prj_id, $res);
-+            } elseif ($current_row > 0) {
-+                // if there are no results, and the page is not the first page reset page to one and reload results
-+                Auth::redirect("list.php?pagerRow=0&rows=$max");
-+            }
-+            $groups = Group::getAssocList($prj_id);
-+            $categories = Category::getAssocList($prj_id);
-+            $column_headings = self::getColumnHeadings($prj_id);
-+            if (count($custom_fields) > 0) {
-+                $column_headings = array_merge($column_headings,$custom_fields);
-+            }
-+            $csv[] = @implode("\t", $column_headings);
-+            for ($i = 0; $i < count($res); $i++) {
-+                $res[$i]["time_spent"] = Misc::getFormattedTime($res[$i]["time_spent"]);
-+                $res[$i]["iss_created_date"] = Date_Helper::getFormattedDate($res[$i]["iss_created_date"]);
-+                $res[$i]["iss_expected_resolution_date"] = Date_Helper::getSimpleDate($res[$i]["iss_expected_resolution_date"], false);
-+                $fields = array(
-+                    $res[$i]['pri_title'],
-+                    $res[$i]['iss_id'],
-+                    $res[$i]['usr_full_name'],
-+                );
-+                // hide the group column from the output if no
-+                // groups are available in the database
-+                if (count($groups) > 0) {
-+                    $fields[] = $res[$i]['group'];
-+                }
-+                $fields[] = $res[$i]['assigned_users'];
-+                $fields[] = $res[$i]['time_spent'];
-+                // hide the category column from the output if no
-+                // categories are available in the database
-+                if (count($categories) > 0) {
-+                    $fields[] = $res[$i]['prc_title'];
-+                }
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    $fields[] = @$res[$i]['customer_title'];
-+                    // check if current user is acustomer and has a per incident contract.
-+                    // if so, check if issue is redeemed.
-+                    if (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer')) {
-+                        if ((Customer::hasPerIncidentContract($prj_id, self::getCustomerID($res[$i]['iss_id'])) &&
-+                                (Customer::isRedeemedIncident($prj_id, $res[$i]['iss_id'])))) {
-+                            $res[$i]['redeemed'] = true;
-+                        }
-+                    }
-+                }
-+                $fields[] = $res[$i]['sta_title'];
-+                $fields[] = $res[$i]["status_change_date"];
-+                $fields[] = $res[$i]["last_action_date"];
-+                $fields[] = $res[$i]['iss_dev_time'];
-+                $fields[] = $res[$i]['iss_summary'];
-+                $fields[] = $res[$i]['iss_expected_resolution_date'];
-+
-+                if (count($custom_fields) > 0) {
-+                    $res[$i]['custom_field'] = array();
-+                    $custom_field_values = Custom_Field::getListByIssue($prj_id, $res[$i]['iss_id']);
-+                    foreach ($custom_field_values as $this_field) {
-+                        if (!empty($custom_fields[$this_field['fld_id']])) {
-+                            $res[$i]['custom_field'][$this_field['fld_id']] = $this_field['value'];
-+                            $fields[] = $this_field['value'];
-+                        }
-+                    }
-+                }
-+
-+                $csv[] = @implode("\t", $fields);
-+            }
-+            $total_pages = ceil($total_rows / $max);
-+            $last_page = $total_pages - 1;
-+            return array(
-+                "list" => $res,
-+                "info" => array(
-+                    "current_page"  => $current_row,
-+                    "start_offset"  => $start,
-+                    "end_offset"    => $start + count($res),
-+                    "total_rows"    => $total_rows,
-+                    "total_pages"   => $total_pages,
-+                    "previous_page" => ($current_row == 0) ? "-1" : ($current_row - 1),
-+                    "next_page"     => ($current_row == $last_page) ? "-1" : ($current_row + 1),
-+                    "last_page"     => $last_page,
-+                    "custom_fields" => $custom_fields
-+                ),
-+                "csv" => @implode("\n", $csv)
-+            );
-+        }
-+    }
-+
-+
-+    /**
-+     * Processes a result set to format the "Last Action Date" column.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     */
-+    function formatLastActionDates(&$result)
-+    {
-+        for ($i = 0; $i < count($result); $i++) {
-+            if (($result[$i]['action_type'] == "internal") &&
-+                    (Auth::getCurrentRole() > User::getRoleID('Customer'))) {
-+                $label = $result[$i]["iss_last_internal_action_type"];
-+                $last_date = $result[$i]["iss_last_internal_action_date"];
-+            } else {
-+                $label = $result[$i]["iss_last_public_action_type"];
-+                $last_date = $result[$i]["iss_last_public_action_date"];
-+            }
-+            $date = new Date($last_date);
-+            $current = new Date(Date_Helper::getCurrentDateGMT());
-+            $result[$i]['last_action_date'] = sprintf("%s: %s ago", ucwords($label),
-+                    Date_Helper::getFormattedDateDiff($current->getDate(DATE_FORMAT_UNIXTIME), $date->getDate(DATE_FORMAT_UNIXTIME)));
-+        }
-+    }
-+
-+
-+    /**
-+     * Retrieves the last status change date for the given issue.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @param   array $result The associative array of data
-+     * @see     self::getListing()
-+     */
-+    function getLastStatusChangeDates($prj_id, &$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_sta_id"];
-+        }
-+        if (count($ids) == 0) {
-+            return false;
-+        }
-+        $customizations = Status::getProjectStatusCustomization($prj_id, $ids);
-+        for ($i = 0; $i < count($result); $i++) {
-+            if (empty($result[$i]['iss_sta_id'])) {
-+                $result[$i]['status_change_date'] = '';
-+            } else {
-+                list($label, $date_field_name) = @$customizations[$result[$i]['iss_sta_id']];
-+                if ((empty($label)) || (empty($date_field_name))) {
-+                    $result[$i]['status_change_date'] = '';
-+                    continue;
-+                }
-+                $current = new Date(Date_Helper::getCurrentDateGMT());
-+                $desc = "$label: %s ago";
-+                $target_date = $result[$i][$date_field_name];
-+                if (empty($target_date)) {
-+                    $result[$i]['status_change_date'] = '';
-+                    continue;
-+                }
-+                $date = new Date($target_date);
-+                $result[$i]['status_change_date'] = sprintf($desc, Date_Helper::getFormattedDateDiff($current->getDate(DATE_FORMAT_UNIXTIME), $date->getDate(DATE_FORMAT_UNIXTIME)));
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues to be displayed in the grid layout.
-+     *
-+     * @access  public
-+     * @param   array $options The search parameters
-+     * @return  string The where clause
-+     */
-+    function buildWhereClause($options)
-+    {
-+        $usr_id = Auth::getUserID();
-+        $prj_id = Auth::getCurrentProject();
-+        $role_id = User::getRoleByUser($usr_id, $prj_id);
-+
-+        $stmt = ' AND iss_usr_id = usr_id';
-+        if ($role_id == User::getRoleID('Customer')) {
-+            $stmt .= " AND iss_customer_id=" . User::getCustomerID($usr_id);
-+        } elseif (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id))) {
-+            $stmt .= " AND (
-+                        iss_usr_id = $usr_id OR
-+                        iur_usr_id = $usr_id
-+                        )";
-+        }
-+
-+        if (!empty($options["users"])) {
-+            $stmt .= " AND (\n";
-+            if (stristr($options["users"], "grp") !== false) {
-+                $chunks = explode(":", $options["users"]);
-+                $stmt .= 'iss_grp_id = ' . Misc::escapeInteger($chunks[1]);
-+            } else {
-+                if ($options['users'] == '-1') {
-+                    $stmt .= 'isu_usr_id IS NULL';
-+                } elseif ($options['users'] == '-2') {
-+                    $stmt .= 'isu_usr_id IS NULL OR isu_usr_id=' . $usr_id;
-+                } elseif ($options['users'] == '-3') {
-+                    $stmt .= 'isu_usr_id = ' . $usr_id . ' OR iss_grp_id = ' . User::getGroupID($usr_id);
-+                } elseif ($options['users'] == '-4') {
-+                    $stmt .= 'isu_usr_id IS NULL OR isu_usr_id = ' . $usr_id . ' OR iss_grp_id = ' . User::getGroupID($usr_id);
-+                } else {
-+                    $stmt .= 'isu_usr_id =' . Misc::escapeInteger($options["users"]);
-+                }
-+            }
-+            $stmt .= ')';
-+        }
-+        if (!empty($options["reporter"])) {
-+            $stmt .= " AND iss_usr_id = " . Misc::escapeInteger($options["reporter"]);
-+        }
-+        if (!empty($options["show_authorized_issues"])) {
-+            $stmt .= " AND (iur_usr_id=$usr_id)";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= " AND (sub_usr_id=$usr_id)";
-+        }
-+        if (!empty($options["keywords"])) {
-+            $stmt .= " AND (\n";
-+            if (($options['search_type'] == 'all_text') && (APP_ENABLE_FULLTEXT)) {
-+                $stmt .= "iss_id IN(" . join(', ', self::getFullTextIssues($options)) . ")";
-+            } elseif (($options['search_type'] == 'customer') && (Customer::hasCustomerIntegration($prj_id))) {
-+                // check if the user is trying to search by customer email
-+                $customer_ids = Customer::getCustomerIDsLikeEmail($prj_id, $options['keywords']);
-+                if (count($customer_ids) > 0) {
-+                    $stmt .= " iss_customer_id IN (" . implode(', ', $customer_ids) . ")";
-+                } else {
-+                    // no results, kill query
-+                    $stmt .= " iss_customer_id = -1";
-+                }
-+            } else {
-+                $stmt .= "(" . Misc::prepareBooleanSearch('iss_summary', $options["keywords"]);
-+                $stmt .= " OR " . Misc::prepareBooleanSearch('iss_description', $options["keywords"]) . ")";
-+            }
-+            $stmt .= "\n) ";
-+        }
-+        if (!empty($options["priority"])) {
-+            $stmt .= " AND iss_pri_id=" . Misc::escapeInteger($options["priority"]);
-+        }
-+        if (!empty($options["status"])) {
-+            $stmt .= " AND iss_sta_id=" . Misc::escapeInteger($options["status"]);
-+        }
-+        if (!empty($options["category"])) {
-+            if (!is_array($options['category'])) {
-+                $options['category'] = array($options['category']);
-+            }
-+            $stmt .= " AND iss_prc_id IN(" . join(', ', Misc::escapeInteger($options["category"])) . ")";
-+        }
-+        if (!empty($options["hide_closed"])) {
-+            $stmt .= " AND sta_is_closed=0";
-+        }
-+        if (!empty($options['release'])) {
-+            $stmt .= " AND iss_pre_id = " . Misc::escapeInteger($options['release']);
-+        }
-+        // now for the date fields
-+        $date_fields = array(
-+            'created_date',
-+            'updated_date',
-+            'last_response_date',
-+            'first_response_date',
-+            'closed_date'
-+        );
-+        foreach ($date_fields as $field_name) {
-+            if (!empty($options[$field_name])) {
-+                switch ($options[$field_name]['filter_type']) {
-+                    case 'greater':
-+                        $stmt .= " AND iss_$field_name >= '" . Misc::escapeString($options[$field_name]['start']) . "'";
-+                        break;
-+                    case 'less':
-+                        $stmt .= " AND iss_$field_name <= '" . Misc::escapeString($options[$field_name]['start']) . "'";
-+                        break;
-+                    case 'between':
-+                        $stmt .= " AND iss_$field_name BETWEEN '" . Misc::escapeString($options[$field_name]['start']) . "' AND '" . Misc::escapeString($options[$field_name]['end']) . "'";
-+                        break;
-+                    case 'null':
-+                        $stmt .= " AND iss_$field_name IS NULL";
-+                        break;
-+                    case 'in_past':
-+                        if (strlen($options[$field_name]['time_period']) == 0) {
-+                            $options[$field_name]['time_period'] = 0;
-+                        }
-+                        $stmt .= " AND (UNIX_TIMESTAMP('" . Date_Helper::getCurrentDateGMT() . "') - UNIX_TIMESTAMP(iss_$field_name)) <= (" .
-+                            Misc::escapeInteger($options[$field_name]['time_period']) . "*3600)";
-+                        break;
-+                }
-+            }
-+        }
-+        // custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                $fld_db_name = Custom_Field::getDBValueFieldNameByType($field['fld_type']);
-+                if (($field['fld_type'] == 'date') &&
-+                        ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . ".icf_iss_id = iss_id";
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . ".icf_fld_id = $fld_id";
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . "." . $fld_db_name . " = $cfo_id";
-+                    }
-+                } elseif ($field['fld_type'] == 'date') {
-+                    if ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day']))) {
-+                        continue;
-+                    }
-+                    $search_value = $search_value['Year'] . "-" . $search_value['Month'] . "-" . $search_value['Day'];
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id AND
-+                        cf" . $fld_id . "." . $fld_db_name . " = '" . Misc::escapeString($search_value) . "')";
-+                } else if ($field['fld_type'] == 'integer') {
-+                    $value = $search_value['value'];
-+                    switch ($search_value['filter_type']) {
-+                        case 'ge': $cmp = '>='; break;
-+                        case 'le': $cmp = '<='; break;
-+                        case 'gt': $cmp = '>'; break;
-+                        case 'lt': $cmp = '<'; break;
-+                        default:   $cmp = '='; break;
-+                    }
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id";
-+                    $stmt .= " AND\n cf" . $fld_id . ".icf_fld_id = $fld_id";
-+                    $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . $cmp . Misc::escapeString($value) . ')';
-+                } else {
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id";
-+                    $stmt .= " AND\n cf" . $fld_id . ".icf_fld_id = $fld_id";
-+                    if ($field['fld_type'] == 'combo') {
-+                        $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . " IN(" . join(', ', Misc::escapeInteger($search_value)) . ")";
-+                    } else {
-+                        $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . " LIKE '%" . Misc::escapeString($search_value) . "%'";
-+                    }
-+                    $stmt .= ')';
-+                }
-+            }
-+        }
-+        // clear cached full-text values if we are not searching fulltext anymore
-+        if ((APP_ENABLE_FULLTEXT) && (@$options['search_type'] != 'all_text')) {
-+            Session::set('fulltext_string', '');
-+            Session::set('fulltext_issues', '');
-+        }
-+        return $stmt;
-+    }
-+
-+
-+    /**
-+     * Method used to get the previous and next issues that are available
-+     * according to the current search parameters.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   array $options The search parameters
-+     * @return  array The list of issues
-+     */
-+    function getSides($issue_id, $options)
-+    {
-+        $usr_id = Auth::getUserID();
-+        $role_id = Auth::getCurrentRole();
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    " . self::getLastActionFields() . "
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user";
-+        // join custom fields if we are searching by custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                if (($field['fld_type'] == 'date') &&
-+                        ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . '_' . $cfo_id . "\n";
-+                    }
-+                } else {
-+                    $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . "\n";
-+                }
-+            }
-+        }
-+        $stmt .= ")";
-+        // check for the custom fields we want to sort by
-+        if (strstr($options['sort_by'], 'custom_field') !== false) {
-+            $fld_id = str_replace("custom_field_", '', $options['sort_by']);
-+            $stmt .= "\n LEFT JOIN \n" .
-+                APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf_sort
-+                ON
-+                    (icf_iss_id = iss_id AND icf_fld_id = $fld_id) \n";
-+        }
-+        if (!empty($options["users"]) || @$options["sort_by"] == "isu_usr_id") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id";
-+        }
-+        if ((!empty($options["show_authorized_issues"])) || (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters(Auth::getCurrentProject())))) {
-+             $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier
-+                 ON
-+                    iur_iss_id=iss_id";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
-+                 ON
-+                    sub_iss_id=iss_id";
-+        }
-+        if (@$options["sort_by"] == "pre_scheduled_date") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id = pre_id";
-+        }
-+        if (@$options['sort_by'] == 'prc_title') {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id = prc_id";
-+        }
-+        $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        $stmt .= self::buildWhereClause($options);
-+        if (strstr($options["sort_by"], 'custom_field') !== false) {
-+            $fld_details = Custom_Field::getDetails($fld_id);
-+            $sort_by = 'cf_sort.' . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']);
-+        } else {
-+            $sort_by = Misc::escapeString($options["sort_by"]);
-+        }
-+        $stmt .= "
-+                 GROUP BY
-+                    iss_id
-+                 ORDER BY
-+                    " . $sort_by . " " . Misc::escapeString($options["sort_order"]) . ",
-+                    iss_id DESC";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            // COMPAT: the next line requires PHP >= 4.0.5
-+            $index = array_search($issue_id, $res);
-+            if (!empty($res[$index+1])) {
-+                $next = $res[$index+1];
-+            }
-+            if (!empty($res[$index-1])) {
-+                $previous = $res[$index-1];
-+            }
-+            return array(
-+                "next"     => @$next,
-+                "previous" => @$previous
-+            );
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of user IDs assigned to a specific
-+     * issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of user IDs
-+     */
-+    function getAssignedUserIDs($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to see if a user is assigned to an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id An integer containg the ID of the user.
-+     * @return  boolean true if the user(s) are assigned to the issue.
-+     */
-+    function isAssignedToUser($issue_id, $usr_id)
-+    {
-+        $assigned_users = self::getAssignedUserIDs($issue_id);
-+        if (in_array($usr_id, $assigned_users)) {
-+            return true;
-+        } else {
-+            return false;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of reporters associated with a given
-+     * list of issues.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getReportersByIssues(&$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        $ids = implode(", ", $ids);
-+        $stmt = "SELECT
-+                    iss_id,
-+                    CONCAT(usr_full_name, ' <', usr_email, '>') AS usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    iss_usr_id=usr_id AND
-+                    iss_id IN ($ids)";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            // now populate the $result variable again
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['reporter'] = $res[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of assigned users by a list
-+     * of issues. This was originally created to optimize the issue
-+     * listing page.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getAssignedUsersByIssues(&$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        if (count($ids) < 1) {
-+            return;
-+        }
-+        $ids = implode(", ", $ids);
-+        $stmt = "SELECT
-+                    isu_iss_id,
-+                    usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_usr_id=usr_id AND
-+                    isu_iss_id IN ($ids)";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            $t = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                if (!empty($t[$res[$i]['isu_iss_id']])) {
-+                    $t[$res[$i]['isu_iss_id']] .= ', ' . $res[$i]['usr_full_name'];
-+                } else {
-+                    $t[$res[$i]['isu_iss_id']] = $res[$i]['usr_full_name'];
-+                }
-+            }
-+            // now populate the $result variable again
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['assigned_users'] = $t[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to add the issue description to a list of issues.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getDescriptionByIssues(&$result)
-+    {
-+        if (count($result) == 0) {
-+            return;
-+        }
-+
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        $ids = implode(", ", $ids);
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_description
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id in ($ids)";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['iss_description'] = $res[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of users (the full names) assigned to a
-+     * specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of users
-+     */
-+    function getAssignedUsers($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the details for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $force_refresh If the cache should not be used.
-+     * @return  array The details for the specified issue
-+     */
-+    function getDetails($issue_id, $force_refresh = false)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (empty($issue_id)) {
-+            return '';
-+        }
-+
-+        if ((!empty($returns[$issue_id])) && ($force_refresh != true)) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue.*,
-+                    prj_title,
-+                    prc_title,
-+                    pre_title,
-+                    pri_title,
-+                    sta_title,
-+                    sta_abbreviation,
-+                    sta_color status_color,
-+                    sta_is_closed
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project
-+                    )
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id=prc_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id=pre_id
-+                 WHERE
-+                    iss_id=$issue_id AND
-+                    iss_prj_id=prj_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            if (empty($res)) {
-+                return "";
-+            } else {
-+                $created_date_ts = Date_Helper::getUnixTimestamp($res['iss_created_date'], Date_Helper::getDefaultTimezone());
-+                // get customer information, if any
-+                if ((!empty($res['iss_customer_id'])) && (Customer::hasCustomerIntegration($res['iss_prj_id']))) {
-+                    $res['customer_business_hours'] = Customer::getBusinessHours($res['iss_prj_id'], $res['iss_customer_id']);
-+                    $res['contact_local_time'] = Date_Helper::getFormattedDate(Date_Helper::getCurrentDateGMT(), $res['iss_contact_timezone']);
-+                    $res['customer_info'] = Customer::getDetails($res['iss_prj_id'], $res['iss_customer_id'], false, $res['iss_customer_contract_id']);
-+                    $res['redeemed_incidents'] = Customer::getRedeemedIncidentDetails($res['iss_prj_id'], $res['iss_id']);
-+                    $max_first_response_time = Customer::getMaximumFirstResponseTime($res['iss_prj_id'], $res['iss_customer_id'], $res['iss_customer_contract_id']);
-+                    $res['max_first_response_time'] = Misc::getFormattedTime($max_first_response_time / 60);
-+                    if (empty($res['iss_first_response_date'])) {
-+                        $first_response_deadline = $created_date_ts + $max_first_response_time;
-+                        if (Date_Helper::getCurrentUnixTimestampGMT() <= $first_response_deadline) {
-+                            $res['max_first_response_time_left'] = Date_Helper::getFormattedDateDiff($first_response_deadline, Date_Helper::getCurrentUnixTimestampGMT());
-+                        } else {
-+                            $res['overdue_first_response_time'] = Date_Helper::getFormattedDateDiff(Date_Helper::getCurrentUnixTimestampGMT(), $first_response_deadline);
-+                        }
-+                    }
-+                }
-+                $res['iss_original_description'] = $res["iss_description"];
-+                if (!strstr($_SERVER["PHP_SELF"], 'update.php')) {
-+                    $res["iss_description"] = nl2br(htmlspecialchars($res["iss_description"]));
-+                    $res["iss_resolution"] = Resolution::getTitle($res["iss_res_id"]);
-+                }
-+                $res["iss_impact_analysis"] = nl2br(htmlspecialchars($res["iss_impact_analysis"]));
-+                $res["iss_created_date"] = Date_Helper::getFormattedDate($res["iss_created_date"]);
-+                $res['iss_created_date_ts'] = $created_date_ts;
-+                $res["assignments"] = @implode(", ", array_values(self::getAssignedUsers($res["iss_id"])));
-+                list($res['authorized_names'], $res['authorized_repliers']) = Authorized_Replier::getAuthorizedRepliers($res["iss_id"]);
-+                $temp = self::getAssignedUsersStatus($res["iss_id"]);
-+                $res["has_inactive_users"] = 0;
-+                $res["assigned_users"] = array();
-+                $res["assigned_inactive_users"] = array();
-+                foreach ($temp as $usr_id => $usr_status) {
-+                    if (!User::isActiveStatus($usr_status)) {
-+                        $res["assigned_inactive_users"][] = $usr_id;
-+                        $res["has_inactive_users"] = 1;
-+                    } else {
-+                        $res["assigned_users"][] = $usr_id;
-+                    }
-+                }
-+                if (@in_array(Auth::getUserID(), $res["assigned_users"])) {
-+                    $res["is_current_user_assigned"] = 1;
-+                } else {
-+                    $res["is_current_user_assigned"] = 0;
-+                }
-+                $res["associated_issues_details"] = self::getAssociatedIssuesDetails($res["iss_id"]);
-+                $res["associated_issues"] = self::getAssociatedIssues($res["iss_id"]);
-+                $res["reporter"] = User::getFullName($res["iss_usr_id"]);
-+                if (empty($res["iss_updated_date"])) {
-+                    $res["iss_updated_date"] = 'not updated yet';
-+                } else {
-+                    $res["iss_updated_date"] = Date_Helper::getFormattedDate($res["iss_updated_date"]);
-+                }
-+                $res["estimated_formatted_time"] = Misc::getFormattedTime($res["iss_dev_time"]);
-+                if (Release::isAssignable($res["iss_pre_id"])) {
-+                    $release = Release::getDetails($res["iss_pre_id"]);
-+                    $res["pre_title"] = $release["pre_title"];
-+                    $res["pre_status"] = $release["pre_status"];
-+                }
-+                // need to return the list of issues that are duplicates of this one
-+                $res["duplicates"] = self::getDuplicateList($res["iss_id"]);
-+                $res["duplicates_details"] = self::getDuplicateDetailsList($res["iss_id"]);
-+                // also get the issue title of the duplicated issue
-+                if (!empty($res['iss_duplicated_iss_id'])) {
-+                    $res['duplicated_issue'] = self::getDuplicatedDetails($res['iss_duplicated_iss_id']);
-+                }
-+
-+                // get group information
-+                if (!empty($res["iss_grp_id"])) {
-+                    $res["group"] = Group::getDetails($res["iss_grp_id"]);
-+                }
-+
-+                // get quarantine issue
-+                $res["quarantine"] = self::getQuarantineInfo($res["iss_id"]);
-+
-+                $returns[$issue_id] = $res;
-+                return $res;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get some simple details about the given duplicated issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The duplicated issue details
-+     */
-+    function getDuplicatedDetails($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_summary title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_sta_id=sta_id AND
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to bulk update a list of issues
-+     *
-+     * @access  public
-+     * @return  boolean
-+     */
-+    function bulkUpdate()
-+    {
-+        // check if user performing this chance has the proper role
-+        if (Auth::getCurrentRole() < User::getRoleID('Manager')) {
-+            return -1;
-+        }
-+
-+        $items = Misc::escapeInteger($_POST['item']);
-+        $new_status_id = Misc::escapeInteger($_POST['status']);
-+        $new_release_id = Misc::escapeInteger(@$_POST['release']);
-+        $new_priority_id = Misc::escapeInteger($_POST['priority']);
-+        $new_category_id = Misc::escapeInteger($_POST['category']);
-+
-+        for ($i = 0; $i < count($items); $i++) {
-+            if (!self::canAccess($items[$i], Auth::getUserID())) {
-+                continue;
-+            } elseif (self::getProjectID($_POST['item'][$i]) != Auth::getCurrentProject()) {
-+                // make sure issue is not in another project
-+                continue;
-+            }
-+
-+            $updated_fields = array();
-+
-+            // update assignment
-+            if (count(@$_POST['users']) > 0) {
-+                $users = Misc::escapeInteger($_POST['users']);
-+                // get who this issue is currently assigned too
-+                $stmt = "SELECT
-+                            isu_usr_id,
-+                            usr_full_name
-+                         FROM
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                         WHERE
-+                            isu_usr_id = usr_id AND
-+                            isu_iss_id = " . $items[$i];
-+                $current_assignees = DB_Helper::getInstance()->getAssoc($stmt);
-+                if (PEAR::isError($current_assignees)) {
-+                    Error_Handler::logError(array($current_assignees->getMessage(), $current_assignees->getDebugInfo()), __FILE__, __LINE__);
-+                    return -1;
-+                }
-+                foreach ($current_assignees as $usr_id => $usr_name) {
-+                    if (!in_array($usr_id, $users)) {
-+                        self::deleteUserAssociation($items[$i], $usr_id, false);
-+                    }
-+                }
-+                $new_user_names = array();
-+                $new_assignees = array();
-+                foreach ($users as $usr_id) {
-+                    $new_user_names[$usr_id] = User::getFullName($usr_id);
-+
-+                    // check if the issue is already assigned to this person
-+                    $stmt = "SELECT
-+                                COUNT(*) AS total
-+                             FROM
-+                                " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                             WHERE
-+                                isu_iss_id=" . $items[$i] . " AND
-+                                isu_usr_id=" . $usr_id;
-+                    $total = DB_Helper::getInstance()->getOne($stmt);
-+                    if ($total > 0) {
-+                        continue;
-+                    } else {
-+                        $new_assignees[] = $usr_id;
-+                        // add the assignment
-+                        self::addUserAssociation(Auth::getUserID(), $items[$i], $usr_id, false);
-+                        Notification::subscribeUser(Auth::getUserID(), $items[$i], $usr_id, Notification::getAllActions());
-+                        Workflow::handleAssignment(Auth::getCurrentProject(), $items[$i], Auth::getUserID());
-+                    }
-+                }
-+                Notification::notifyNewAssignment($new_assignees, $items[$i]);
-+                $updated_fields['Assignment'] = History::formatChanges(join(', ', $current_assignees), join(', ', $new_user_names));
-+            }
-+
-+            // update status
-+            if (!empty($new_status_id)) {
-+                $old_status_id = self::getStatusID($items[$i]);
-+                $res = self::setStatus($items[$i], $new_status_id, false);
-+                if ($res == 1) {
-+                    $updated_fields['Status'] = History::formatChanges(Status::getStatusTitle($old_status_id), Status::getStatusTitle($new_status_id));
-+                }
-+            }
-+
-+            // update release
-+            if (!empty($new_release_id)) {
-+                $old_release_id = self::getRelease($items[$i]);
-+                $res = self::setRelease($items[$i], $new_release_id);
-+                if ($res == 1) {
-+                    $updated_fields['Release'] = History::formatChanges(Release::getTitle($old_release_id), Release::getTitle($new_release_id));
-+                }
-+            }
-+
-+            // update priority
-+            if (!empty($new_priority_id)) {
-+                $old_priority_id = self::getPriority($items[$i]);
-+                $res = self::setPriority($items[$i], $new_priority_id);
-+                if ($res == 1) {
-+                    $updated_fields['Priority'] = History::formatChanges(Priority::getTitle($old_priority_id), Priority::getTitle($new_priority_id));
-+                }
-+            }
-+
-+            // update category
-+            if (!empty($new_category_id)) {
-+                $old_category_id = self::getCategory($items[$i]);
-+                $res = self::setCategory($items[$i], $new_category_id);
-+                if ($res == 1) {
-+                    $updated_fields['Category'] = History::formatChanges(Category::getTitle($old_category_id), Category::getTitle($new_category_id));
-+                }
-+            }
-+
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $k = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($k > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    $changes .= "$key: $value";
-+                    $k++;
-+                }
-+                History::add($items[$i], Auth::getUserID(), History::getTypeID('issue_bulk_updated'), "Issue updated ($changes) by " . User::getFullName(Auth::getUserID()));
-+            }
-+
-+            // close if request
-+            if ((isset($_REQUEST['closed_status'])) && (!empty($_REQUEST['closed_status']))) {
-+                self::close(Auth::getUserID(), $items[$i], true, 0, Misc::escapeInteger($_REQUEST['closed_status']), Misc::escapeString($_REQUEST['closed_message']), $_REQUEST['notification_list']);
-+            }
-+        }
-+        return true;
-+    }
-+
-+
-+    /**
-+     * Method used to set the initial impact analysis for a specific issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setImpactAnalysis($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='update',
-+                    iss_developer_est_time=" . Misc::escapeInteger($_POST["dev_time"]) . ",
-+                    iss_impact_analysis='" . Misc::escapeString($_POST["impact_analysis"]) . "'
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // add the impact analysis to the history of the issue
-+            $summary = 'Initial Impact Analysis for issue set by ' . User::getFullName(Auth::getUserID());
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('impact_analysis_added'), $summary);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs that area available in the
-+     * system.
-+     *
-+     * @access  public
-+     * @param   string $extra_condition An extra condition in the WHERE clause
-+     * @return  array The list of issue IDs
-+     */
-+    function getColList($extra_condition = NULL)
-+    {
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        if (!empty($extra_condition)) {
-+            $stmt .= " AND $extra_condition ";
-+        }
-+        $stmt .= "
-+                 ORDER BY
-+                    iss_id DESC";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs and their respective
-+     * titles.
-+     *
-+     * @access  public
-+     * @param   string $extra_condition An extra condition in the WHERE clause
-+     * @return  array The list of issues
-+     */
-+    function getAssocList($extra_condition = NULL)
-+    {
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        if (!empty($extra_condition)) {
-+            $stmt .= " AND $extra_condition ";
-+        }
-+        $stmt .= "
-+                 ORDER BY
-+                    iss_id ASC";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues associated to a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of associated issues
-+     */
-+    function getAssociatedIssues($issue_id)
-+    {
-+        $issues = self::getAssociatedIssuesDetails($issue_id);
-+        $associated = array();
-+        for ($i = 0; $i < count($issues); $i++) {
-+            $associated[] = $issues[$i]['associated_issue'];
-+        }
-+        return $associated;
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues associated details to a
-+     * specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of associated issues
-+     */
-+    function getAssociatedIssuesDetails($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    isa_associated_id associated_issue,
-+                    iss_summary associated_title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    isa_associated_id=iss_id AND
-+                    iss_sta_id=sta_id AND
-+                    isa_issue_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to check whether an issue was already closed or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  boolean
-+     */
-+    function isClosed($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    iss_sta_id=sta_id AND
-+                    sta_is_closed=1";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns a simple list of issues that are currently set to some
-+     * form of quarantine. This is mainly used by the IRC interface.
-+     *
-+     * @access  public
-+     * @return  array List of quarantined issues
-+     */
-+    function getQuarantinedIssueList()
-+    {
-+        // XXX: would be nice to restrict the result list to only one project
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id=iss_id AND
-+                    iqu_expiration >= '" . Date_Helper::getCurrentDateGMT() . "' AND
-+                    iqu_expiration IS NOT NULL";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            self::getAssignedUsersByIssues($res);
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the status of a quarantine.
-+     *
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer Indicates what the current state of quarantine is.
-+     */
-+    function getQuarantineInfo($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iqu_status,
-+                    iqu_expiration
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id = " . Misc::escapeInteger($issue_id) . " AND
-+                        (iqu_expiration > '" . Date_Helper::getCurrentDateGMT() . "' OR
-+                        iqu_expiration IS NULL)";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            if (!empty($res["iqu_expiration"])) {
-+                $expiration_ts = Date_Helper::getUnixTimestamp($res['iqu_expiration'], Date_Helper::getDefaultTimezone());
-+                $res["time_till_expiration"] = Date_Helper::getFormattedDateDiff($expiration_ts, Date_Helper::getCurrentUnixTimestampGMT());
-+            }
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Sets the quarantine status. Optionally an expiration date can be set
-+     * to indicate when the quarantine expires. A status > 0 indicates that quarantine is active.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $status The quarantine status
-+     * @param   string  $expiration The expiration date of quarantine (default empty)
-+     */
-+    function setQuarantine($issue_id, $status, $expiration = '')
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $status = Misc::escapeInteger($status);
-+
-+        // see if there is an existing record
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id = $issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        if ($res > 0) {
-+            // update
-+            $stmt = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                     SET
-+                        iqu_status = $status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\niqu_expiration = '" . Misc::escapeString($expiration) . "'";
-+            }
-+            $stmt .= "\nWHERE
-+                        iqu_iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                // add history entry about this change taking place
-+                if ($status == 0) {
-+                    History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_quarantine_removed'),
-+                            "Issue quarantine status cleared by " . User::getFullName(Auth::getUserID()));
-+                }
-+            }
-+        } else {
-+            // insert
-+            $stmt = "INSERT INTO
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                     (
-+                        iqu_iss_id,
-+                        iqu_status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\niqu_expiration\n";
-+            }
-+            $stmt .= ") VALUES (
-+                        $issue_id,
-+                        $status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\n'" . Misc::escapeString($expiration) . "'\n";
-+            }
-+            $stmt .= ")";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Sets the group of the issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $group_id The ID of the group
-+     * @return  integer 1 if successful, -1 or -2 otherwise
-+     */
-+    function setGroup($issue_id, $group_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $group_id = Misc::escapeInteger($group_id);
-+
-+        $current = self::getDetails($issue_id);
-+        if ($current["iss_grp_id"] == $group_id) {
-+            return -2;
-+        }
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_grp_id = $group_id
-+                 WHERE
-+                    iss_id = $issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        $current_user = Auth::getUserID();
-+        if (empty($current_user)) {
-+            $current_user = APP_SYSTEM_USER_ID;
-+        }
-+        History::add($issue_id, $current_user, History::getTypeID('group_changed'),
-+                "Group changed (" . History::formatChanges(Group::getName($current["iss_grp_id"]), Group::getName($group_id)) . ") by " . User::getFullName($current_user));
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Returns the group ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The associated group ID
-+     */
-+    function getGroupID($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_grp_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns an array of issues based on full text search results.
-+     *
-+     * @param   array $options An array of search options
-+     * @return  array An array of issue IDS
-+     */
-+    function getFullTextIssues($options)
-+    {
-+        // check if a list of issues for this full text search is already cached
-+        $fulltext_string = Session::get('fulltext_string');
-+        if ((!empty($fulltext_string)) && ($fulltext_string == $options['keywords'])) {
-+            return Session::get('fulltext_issues');
-+        }
-+
-+        // no pre-existing list, generate them
-+        $stmt = "(SELECT
-+                    DISTINCT(iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                     MATCH(iss_summary, iss_description) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(not_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "note
-+                 WHERE
-+                     MATCH(not_note) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(ttr_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking
-+                 WHERE
-+                     MATCH(ttr_summary) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(phs_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "phone_support
-+                 WHERE
-+                     MATCH(phs_description) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                     DISTINCT(sup_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
-+                 WHERE
-+                     sup_id = seb_sup_id AND
-+                     MATCH(seb_body) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 )";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array(-1);
-+        } else {
-+            $stmt = "SELECT
-+                        DISTINCT(icf_iss_id)
-+                    FROM
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
-+                    WHERE
-+                        MATCH (icf_value) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)";
-+            $custom_res = DB_Helper::getInstance()->getCol($stmt);
-+            if (PEAR::isError($custom_res)) {
-+                Error_Handler::logError(array($custom_res->getMessage(), $custom_res->getDebugInfo()), __FILE__, __LINE__);
-+                return array(-1);
-+            }
-+            $issues = array_merge($res, $custom_res);
-+            // we kill the query results on purpose to flag that no
-+            // issues could be found with fulltext search
-+            if (count($issues) < 1) {
-+                $issues = array(-1);
-+            }
-+            Session::set('fulltext_string', $options['keywords']);
-+            Session::set('fulltext_issues', $issues);
-+            return $issues;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method to determine if user can access a particular issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue.
-+     * @param   integer $usr_id The ID of the user
-+     * @return  boolean If the user can access the issue
-+     */
-+    function canAccess($issue_id, $usr_id)
-+    {
-+        static $access;
-+
-+        if (empty($issue_id)) {
-+            return true;
-+        }
-+
-+        if (isset($access[$issue_id . "-" . $usr_id])) {
-+            return $access[$issue_id . "-" . $usr_id];
-+        }
-+
-+        $details = self::getDetails($issue_id);
-+        if (empty($details)) {
-+            return true;
-+        }
-+        $usr_details = User::getDetails($usr_id);
-+        $usr_role = User::getRoleByUser($usr_id, $details['iss_prj_id']);
-+        $prj_id = self::getProjectID($issue_id);
-+
-+
-+        if (empty($usr_role)) {
-+            // check if they are even allowed to access the project
-+            $return = false;
-+        } elseif ((Customer::hasCustomerIntegration($details['iss_prj_id'])) && ($usr_role == User::getRoleID("Customer")) &&
-+                ($details['iss_customer_id'] != $usr_details['usr_customer_id'])) {
-+            // check customer permissions
-+            $return = false;
-+        } elseif ($details['iss_private'] == 1) {
-+            // check if the issue is even private
-+
-+            // check role, reporter, assigment and group
-+            if ($usr_role > User::getRoleID("Developer")) {
-+                $return = true;
-+            } elseif ($details['iss_usr_id'] == $usr_id) {
-+                $return = true;
-+            } elseif (self::isAssignedToUser($issue_id, $usr_id)) {
-+                $return = true;
-+            } elseif ((!empty($details['iss_grp_id'])) && (!empty($usr_details['usr_grp_id'])) &&
-+                        ($details['iss_grp_id'] == $usr_details['usr_grp_id'])) {
-+                $return = true;
-+            } elseif (Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id)) {
-+                $return = true;
-+            } else {
-+                $return = false;
-+            }
-+        } elseif ((Auth::getCurrentRole() == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id)) &&
-+                ($details['iss_usr_id'] != $usr_id) && (!Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id))) {
-+            return false;
-+        } else {
-+            $return = true;
-+        }
-+
-+        $access[$issue_id . "-" . $usr_id] = $return;
-+        return $return;
-+    }
-+
-+
-+    /**
-+     * Returns true if the specified issue is private, false otherwise
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  boolean If the issue is private or not
-+     */
-+    function isPrivate($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!isset($returns[$issue_id])) {
-+            $sql = "SELECT
-+                        iss_private
-+                    FROM
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    WHERE
-+                        iss_id=$issue_id";
-+            $res = DB_Helper::getInstance()->getOne($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return true;
-+            } else {
-+                if ($res == 1) {
-+                    $returns[$issue_id] = true;
-+                } else {
-+                    $returns[$issue_id] = false;
-+                }
-+            }
-+        }
-+        return $returns[$issue_id];
-+    }
-+
-+
-+    /**
-+     * Clears closed information from an issues.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     */
-+    function clearClosed($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_closed_date = null,
-+                    iss_res_id = null
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the message ID that should be used as the parent ID for all messages
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     */
-+    function getRootMessageID($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_root_message_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the issue ID of the issue with the specified root message ID, or false
-+     * @access  public
-+     * @param   string $msg_id The Message ID
-+     * @return  integer The ID of the issue
-+     */
-+    function getIssueByRootMessageID($msg_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$msg_id])) {
-+            return $returns[$msg_id];
-+        }
-+        $sql = "SELECT
-+                    iss_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_root_message_id = '" . Misc::escapeString($msg_id) . "'";
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        }
-+        if (empty($res)) {
-+            $returns[$msg_id] = false;
-+        } else {
-+            $returns[$msg_id] =  $res;
-+        }
-+        return $returns[$msg_id];
-+    }
-+
-+
-+    /**
-+     * Sets the assignees for the issue
-+     *
-+     * @param   integer $issue_id
-+     * @param   array   $assignees
-+     */
-+    function setAssignees($issue_id, $assignees)
-+    {
-+        if (!is_array($assignees)) {
-+            $assignees = array();
-+        }
-+
-+        // see if there is anything to change
-+        $old_assignees = self::getAssignedUserIDs($issue_id);
-+        if ((count(array_diff($old_assignees, $assignees)) == 0) && (count(array_diff($assignees, $old_assignees)) == 0)) {
-+            return;
-+        }
-+
-+        $old_assignee_names = self::getAssignedUsers($issue_id);
-+
-+        Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, Auth::getUserID(), self::getDetails($issue_id), $assignees, true);
-+        // clear up the assignments for this issue, and then assign it to the current user
-+        self::deleteUserAssociations($issue_id);
-+        $assignee_names = array();
-+        foreach ($assignees as $assignee) {
-+            $res = self::addUserAssociation(Auth::getUserID(), $issue_id, $assignee, false);
-+            if ($res == -1) {
-+                return false;
-+            }
-+            $assignee_names[] = User::getFullName($assignee);
-+            Notification::subscribeUser(Auth::getUserID(), $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'set_assignees'), false);
-+        }
-+
-+        Notification::notifyNewAssignment($assignees, $issue_id);
-+
-+        // save a history entry about this...
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('user_associated'),
-+                        "Issue assignment to changed (" . History::formatChanges(join(', ', $old_assignee_names), join(', ', $assignee_names)) . ") by " . User::getFullName(Auth::getUserID()));
-+    }
-+}
---- eventum-2.2/lib/eventum/class.issue.php.~2~        1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/lib/eventum/class.issue.php.~2~  2009-10-12 22:10:36.432519927 +0300
-@@ -0,0 +1,4748 @@
-+<?php
-+/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
-+// +----------------------------------------------------------------------+
-+// | Eventum - Issue Tracking System                                      |
-+// +----------------------------------------------------------------------+
-+// | Copyright (c) 2003 - 2008 MySQL AB                                   |
-+// | Copyright (c) 2008 - 2009 Sun Microsystem Inc.                       |
-+// |                                                                      |
-+// | This program is free software; you can redistribute it and/or modify |
-+// | it under the terms of the GNU General Public License as published by |
-+// | the Free Software Foundation; either version 2 of the License, or    |
-+// | (at your option) any later version.                                  |
-+// |                                                                      |
-+// | This program is distributed in the hope that it will be useful,      |
-+// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
-+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
-+// | GNU General Public License for more details.                         |
-+// |                                                                      |
-+// | You should have received a copy of the GNU General Public License    |
-+// | along with this program; if not, write to:                           |
-+// |                                                                      |
-+// | Free Software Foundation, Inc.                                       |
-+// | 59 Temple Place - Suite 330                                          |
-+// | Boston, MA 02111-1307, USA.                                          |
-+// +----------------------------------------------------------------------+
-+// | Authors: João Prado Maia <jpm@mysql.com>                             |
-+// +----------------------------------------------------------------------+
-+//
-+
-+
-+/**
-+ * Class designed to handle all business logic related to the issues in the
-+ * system, such as adding or updating them or listing them in the grid mode.
-+ *
-+ * @author  João Prado Maia <jpm@mysql.com>
-+ * @version $Revision$
-+ */
-+
-+class Issue
-+{
-+    /**
-+     * Method used to check whether a given issue ID exists or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $check_project If we should check that this issue is in the current project
-+     * @return  boolean
-+     */
-+    function exists($issue_id, $check_project = true)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        if ($check_project) {
-+            $stmt .= " AND
-+                    iss_prj_id = " . Auth::getCurrentProject();
-+        }
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of column heading titles for the
-+     * CSV export functionality of the issue listing screen.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @return  array The list of column heading titles
-+     */
-+    function getColumnHeadings($prj_id)
-+    {
-+        $headings = array(
-+            'Priority',
-+            'Issue ID',
-+            'Reporter',
-+        );
-+        // hide the group column from the output if no
-+        // groups are available in the database
-+        $groups = Group::getAssocList($prj_id);
-+        if (count($groups) > 0) {
-+            $headings[] = 'Group';
-+        }
-+        $headings[] = 'Assigned';
-+        $headings[] = 'Time Spent';
-+        // hide the category column from the output if no
-+        // categories are available in the database
-+        $categories = Category::getAssocList($prj_id);
-+        if (count($categories) > 0) {
-+            $headings[] = 'Category';
-+        }
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $headings[] = 'Customer';
-+        }
-+        $headings[] = 'Status';
-+        $headings[] = 'Status Change Date';
-+        $headings[] = 'Last Action Date';
-+        $headings[] = 'Est. Dev. TIme';
-+        $headings[] = 'Summary';
-+        $headings[] = 'Expected Resolution Date';
-+        return $headings;
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of date fields available to issues, to
-+     * be used when customizing the issue listing screen in the 'last status
-+     * change date' column.
-+     *
-+     * @access  public
-+     * @param   boolean $display_customer_fields Whether to include any customer related fields or not
-+     * @return  array The list of available date fields
-+     */
-+    function getDateFieldsAssocList($display_customer_fields = FALSE)
-+    {
-+        $fields = array(
-+            'iss_created_date'              => 'Created Date',
-+            'iss_updated_date'              => 'Last Updated Date',
-+            'iss_last_response_date'        => 'Last Response Date',
-+            'iss_closed_date'               => 'Closed Date'
-+        );
-+        if ($display_customer_fields) {
-+            $fields['iss_last_customer_action_date'] = 'Customer Action Date';
-+        }
-+        asort($fields);
-+        return $fields;
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs and their respective
-+     * titles associated to a given project.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @return  array The list of issues
-+     */
-+    function getAssocListByProject($prj_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Misc::escapeInteger($prj_id) . "
-+                 ORDER BY
-+                    iss_id ASC";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The status ID
-+     */
-+    function getStatusID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_sta_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Records the last customer action date for a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function recordLastCustomerAction($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_last_customer_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='customer action'
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the customer ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getCustomerID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the contract ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getContractID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_contract_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Sets the contract ID for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer The contract ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setContractID($issue_id, $contract_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $old_contract_id = self::getContractID($issue_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                SET
-+                    iss_customer_contract_id = " . Misc::escapeInteger($contract_id) . "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // log this
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID("contract_changed"), "Contract changed from $old_contract_id to $contract_id by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the customer ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getContactID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_contact_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the project associated to a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $force_refresh If the cache should not be used.
-+     * @return  integer The project ID
-+     */
-+    function getProjectID($issue_id, $force_refresh = false)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if ((!empty($returns[$issue_id])) && ($force_refresh != true)) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_prj_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remotely assign a given issue to an user.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing the change
-+     * @param   boolean $assignee The user ID of the assignee
-+     * @return  integer The status ID
-+     */
-+    function remoteAssign($issue_id, $usr_id, $assignee)
-+    {
-+        Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, $usr_id, self::getDetails($issue_id), array($assignee), true);
-+        // clear up the assignments for this issue, and then assign it to the current user
-+        self::deleteUserAssociations($issue_id, $usr_id);
-+        $res = self::addUserAssociation($usr_id, $issue_id, $assignee, false);
-+        if ($res != -1) {
-+            // save a history entry about this...
-+            History::add($issue_id, $usr_id, History::getTypeID('remote_assigned'), "Issue remotely assigned to " . User::getFullName($assignee) . " by " . User::getFullName($usr_id));
-+            Notification::subscribeUser($usr_id, $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'remote_assign'), false);
-+            if ($assignee != $usr_id) {
-+                Notification::notifyNewAssignment(array($assignee), $issue_id);
-+            }
-+        }
-+        return $res;
-+    }
-+
-+
-+    /**
-+     * Method used to set the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $status_id The new status ID
-+     * @param   boolean $notify If a notification should be sent about this change.
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setStatus($issue_id, $status_id, $notify = false)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+
-+        $workflow = Workflow::preStatusChange(self::getProjectID($issue_id), $issue_id, $status_id, $notify);
-+        if ($workflow !== true) {
-+            return $workflow;
-+        }
-+
-+        // check if the status is already set to the 'new' one
-+        if (self::getStatusID($issue_id) == $status_id) {
-+            return -1;
-+        }
-+
-+        $old_status = self::getStatusID($issue_id);
-+        $old_details = Status::getDetails($old_status);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_sta_id=$status_id,
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='update'
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // clear out the last-triggered-reminder flag when changing the status of an issue
-+            Reminder_Action::clearLastTriggered($issue_id);
-+
-+            // if old status was closed and new status is not, clear closed data from issue.
-+            if (@$old_details['sta_is_closed'] == 1) {
-+                $new_details = Status::getDetails($status_id);
-+                if ($new_details['sta_is_closed'] != 1) {
-+                    self::clearClosed($issue_id);
-+                }
-+            }
-+
-+            if ($notify) {
-+                Notification::notifyStatusChange($issue_id, $old_status, $status_id);
-+            }
-+
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remotely set the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing this change
-+     * @param   integer $new_status The new status ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setRemoteStatus($issue_id, $usr_id, $new_status)
-+    {
-+        $sta_id = Status::getStatusID($new_status);
-+
-+        $res = self::setStatus($issue_id, $sta_id);
-+        if ($res == 1) {
-+            // record history entry
-+            History::add($issue_id, $usr_id, History::getTypeID('remote_status_change'), "Status remotely changed to '$new_status' by " . User::getFullName($usr_id));
-+        }
-+        return $res;
-+    }
-+
-+
-+    /**
-+     * Method used to set the release of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $pre_id The ID of the release to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setRelease($issue_id, $pre_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $pre_id = Misc::escapeInteger($pre_id);
-+
-+        if ($pre_id != self::getRelease($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_pre_id = $pre_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current release of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The release
-+     */
-+    function getRelease($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_pre_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to set the priority of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $pri_id The ID of the priority to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setPriority($issue_id, $pri_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $pri_id = Misc::escapeInteger($pri_id);
-+
-+        if ($pri_id != self::getPriority($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_pri_id = $pri_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current issue priority
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The priority
-+     */
-+    function getPriority($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_pri_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to set the category of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $prc_id The ID of the category to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setCategory($issue_id, $prc_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $prc_id = Misc::escapeInteger($prc_id);
-+
-+        if ($prc_id != self::getPriority($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_prc_id = $prc_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current issue category
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The category
-+     */
-+    function getCategory($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_prc_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get all issues associated with a status that doesn't have
-+     * the 'closed' context.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID to list issues from
-+     * @param   integer $usr_id The user ID of the user requesting this information
-+     * @param   boolean $show_all_issues Whether to show all open issues, or just the ones assigned to the given email address
-+     * @param   integer $status_id The status ID to be used to restrict results
-+     * @return  array The list of open issues
-+     */
-+    function getOpenIssues($prj_id, $usr_id, $show_all_issues, $status_id)
-+    {
-+        $prj_id = Misc::escapeInteger($prj_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+        $projects = Project::getRemoteAssocListByUser($usr_id);
-+        if (@count($projects) == 0) {
-+            return '';
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary,
-+                    sta_title
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                    )
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id
-+                 WHERE ";
-+        if (!empty($status_id)) {
-+            $stmt .= " sta_id=$status_id AND ";
-+        }
-+        $stmt .= "
-+                    iss_prj_id=$prj_id AND
-+                    sta_id=iss_sta_id AND
-+                    sta_is_closed=0";
-+        if ($show_all_issues == false) {
-+            $stmt .= " AND
-+                    isu_usr_id=$usr_id";
-+        }
-+        $stmt .= "\nGROUP BY
-+                        iss_id";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            if (count($res) > 0) {
-+                self::getAssignedUsersByIssues($res);
-+            }
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to build the required parameters to simulate an email reply
-+     * to the user who reported the issue, using the issue details like summary
-+     * and description as email fields.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The email parameters
-+     */
-+    function getReplyDetails($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $stmt = "SELECT
-+                    iss_created_date,
-+                    usr_full_name AS reporter,
-+                    usr_email AS reporter_email,
-+                    iss_description AS description,
-+                    iss_summary AS sup_subject
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    iss_usr_id=usr_id AND
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $res['reply_subject'] = 'Re: [#' . $issue_id . '] ' . $res["sup_subject"];
-+            $res['created_date_ts'] = Date_Helper::getUnixTimestamp($res['iss_created_date'], 'GMT');
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to record the last updated timestamp for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   string $type The type of update that was made (optional)
-+     * @return  boolean
-+     */
-+    function markAsUpdated($issue_id, $type = false)
-+    {
-+        $public = array("staff response", "customer action", "file uploaded", "user response");
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "'\n";
-+        if ($type != false) {
-+            if (in_array($type, $public)) {
-+                $field = "iss_last_public_action_";
-+            } else {
-+                $field = "iss_last_internal_action_";
-+            }
-+            $stmt .= ",\n " . $field . "date = '" . Date_Helper::getCurrentDateGMT() . "',\n" .
-+                $field . "type  ='" . Misc::escapeString($type) . "'\n";
-+        }
-+        $stmt .= "WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            // update last response dates if this is a staff response
-+            if ($type == "staff response") {
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         SET
-+                            iss_last_response_date='" . Date_Helper::getCurrentDateGMT() . "'
-+                         WHERE
-+                            iss_id = " . Misc::escapeInteger($issue_id);
-+                DB_Helper::getInstance()->query($stmt);
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         SET
-+                            iss_first_response_date='" . Date_Helper::getCurrentDateGMT() . "'
-+                         WHERE
-+                            iss_first_response_date IS NULL AND
-+                            iss_id = " . Misc::escapeInteger($issue_id);
-+                DB_Helper::getInstance()->query($stmt);
-+            }
-+
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to check whether a given issue has duplicates
-+     * or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  boolean
-+     */
-+    function hasDuplicates($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(iss_id)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to update the duplicated issues for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function updateDuplicates($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $ids = self::getDuplicateList($issue_id);
-+        if ($ids == '') {
-+            return -1;
-+        }
-+        $ids = @array_keys($ids);
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_prc_id=" . Misc::escapeInteger($_POST["category"]) . ",";
-+        if (@$_POST["keep"] == "no") {
-+            $stmt .= "iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",";
-+        }
-+        $stmt .= "
-+                    iss_pri_id=" . Misc::escapeInteger($_POST["priority"]) . ",
-+                    iss_sta_id=" . Misc::escapeInteger($_POST["status"]) . ",
-+                    iss_res_id=" . Misc::escapeInteger($_POST["resolution"]) . "
-+                 WHERE
-+                    iss_id IN (" . implode(", ", $ids) . ")";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // record the change
-+            for ($i = 0; $i < count($ids); $i++) {
-+                History::add($ids[$i], Auth::getUserID(), History::getTypeID('duplicate_update'),
-+                    "The details for issue #$issue_id were updated by " . User::getFullName(Auth::getUserID()) . " and the changes propagated to the duplicated issues.");
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get a list of the duplicate issues for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of duplicates
-+     */
-+    function getDuplicateList($issue_id)
-+    {
-+        $res = self::getDuplicateDetailsList($issue_id);
-+        if (@count($res) == 0) {
-+            return '';
-+        } else {
-+            $list = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                $list[$res[$i]['issue_id']] = $res[$i]['title'];
-+            }
-+            return $list;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get a list of the duplicate issues (and their details)
-+     * for a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of duplicates
-+     */
-+    function getDuplicateDetailsList($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_id issue_id,
-+                    iss_summary title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_sta_id=sta_id AND
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to clear the duplicate status of an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function clearDuplicateStatus($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_duplicated_iss_id=NULL
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // record the change
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('duplicate_removed'), "Duplicate flag was reset by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to mark an issue as a duplicate of an existing one.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function markAsDuplicate($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (!self::exists($issue_id)) {
-+            return -1;
-+        }
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($_POST["duplicated_issue"]) . "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if (!empty($_POST["comments"])) {
-+                // add note with the comments of marking an issue as a duplicate of another one
-+                $_POST['title'] = 'Issue duplication comments';
-+                $_POST["note"] = $_POST["comments"];
-+                Note::insert(Auth::getUserID(), $issue_id);
-+            }
-+            // record the change
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('duplicate_added'),
-+                    "Issue marked as a duplicate of issue #" . $_POST["duplicated_issue"] . " by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    function isDuplicate($issue_id)
-+    {
-+        $sql = "SELECT
-+                    count(iss_id)
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id) . " AND
-+                    iss_duplicated_iss_id IS NULL";
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        }
-+        if ($res > 0) {
-+            return false;
-+        } else {
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get an associative array of user ID => user
-+     * status associated with a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of users
-+     */
-+    function getAssignedUsersStatus($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_id,
-+                    usr_status
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the summary associated with a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  string The issue summary
-+     */
-+    function getTitle($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the issue ID associated with a specific summary.
-+     *
-+     * @access  public
-+     * @param   string $summary The summary to look for
-+     * @return  integer The issue ID
-+     */
-+    function getIssueID($summary)
-+    {
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_summary='" . Misc::escapeString($summary) . "'";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            if (empty($res)) {
-+                return 0;
-+            } else {
-+                return $res;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to add a new anonymous based issue in the system.
-+     *
-+     * @access  public
-+     * @return  integer The new issue ID
-+     */
-+    function addAnonymousReport()
-+    {
-+        $options = Project::getAnonymousPostOptions($_POST["project"]);
-+        $initial_status = Project::getInitialStatus($_POST["project"]);
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 (
-+                    iss_prj_id,
-+                    iss_prc_id,
-+                    iss_pre_id,
-+                    iss_pri_id,
-+                    iss_usr_id,";
-+        if (!empty($initial_status)) {
-+            $stmt .= "iss_sta_id,";
-+        }
-+        $stmt .= "
-+                    iss_created_date,
-+                    iss_last_public_action_date,
-+                    iss_last_public_action_type,
-+                    iss_summary,
-+                    iss_description,
-+                    iss_root_message_id
-+                 ) VALUES (
-+                    " . Misc::escapeInteger($_POST["project"]) . ",
-+                    " . $options["category"] . ",
-+                    0,
-+                    " . $options["priority"] . ",
-+                    " . $options["reporter"] . ",";
-+        if (!empty($initial_status)) {
-+            $stmt .= "$initial_status,";
-+        }
-+        $stmt .= "
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    'created',
-+                    '" . Misc::escapeString($_POST["summary"]) . "',
-+                    '" . Misc::escapeString($_POST["description"]) . "',
-+                    '" . Misc::escapeString(Mail_Helper::generateMessageID()) . "'
-+                 )";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return $res;
-+        } else {
-+            $new_issue_id = DB_Helper::get_last_insert_id();
-+            // log the creation of the issue
-+            History::add($new_issue_id, APP_SYSTEM_USER_ID, History::getTypeID('issue_opened_anon'), 'Issue opened anonymously');
-+
-+            // now process any files being uploaded
-+            $found = 0;
-+            for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+                if (!@empty($_FILES["file"]["name"][$i])) {
-+                    $found = 1;
-+                    break;
-+                }
-+            }
-+            if ($found) {
-+                $attachment_id = Attachment::add($new_issue_id, $options["reporter"], 'files uploaded anonymously');
-+                for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+                    $filename = @$_FILES["file"]["name"][$i];
-+                    if (empty($filename)) {
-+                        continue;
-+                    }
-+                    $blob = file_get_contents($_FILES["file"]["tmp_name"][$i]);
-+                    if (!empty($blob)) {
-+                        Attachment::addFile($attachment_id, $filename, $_FILES["file"]["type"][$i], $blob);
-+                    }
-+                }
-+            }
-+            // need to process any custom fields ?
-+            if (@count($_POST["custom_fields"]) > 0) {
-+                foreach ($_POST["custom_fields"] as $fld_id => $value) {
-+                    Custom_Field::associateIssue($new_issue_id, $fld_id, $value);
-+                }
-+            }
-+
-+            // now add the user/issue association
-+            $assign = array();
-+            $users = @$options["users"];
-+            $actions = Notification::getDefaultActions($new_issue_id, false, 'anon_issue');
-+            for ($i = 0; $i < count($users); $i++) {
-+                Notification::subscribeUser(APP_SYSTEM_USER_ID, $new_issue_id, $users[$i], $actions);
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $new_issue_id, $users[$i]);
-+                $assign[] = $users[$i];
-+            }
-+
-+            Workflow::handleNewIssue(Misc::escapeInteger($_POST["project"]),  $new_issue_id, false, false);
-+
-+            // also notify any users that want to receive emails anytime a new issue is created
-+            Notification::notifyNewIssue($_POST['project'], $new_issue_id);
-+
-+            return $new_issue_id;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove all issues associated with a specific list of
-+     * projects.
-+     *
-+     * @access  public
-+     * @param   array $ids The list of projects to look for
-+     * @return  boolean
-+     */
-+    function removeByProjects($ids)
-+    {
-+        $items = @implode(", ", Misc::escapeInteger($ids));
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id IN ($items)";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if (count($res) > 0) {
-+                self::deleteAssociations($res);
-+                Attachment::removeByIssues($res);
-+                SCM::removeByIssues($res);
-+                Impact_Analysis::removeByIssues($res);
-+                self::deleteUserAssociations($res);
-+                Note::removeByIssues($res);
-+                Time_Tracking::removeByIssues($res);
-+                Notification::removeByIssues($res);
-+                Custom_Field::removeByIssues($res);
-+                Phone_Support::removeByIssues($res);
-+                History::removeByIssues($res);
-+                // now really delete the issues
-+                $items = implode(", ", $res);
-+                $stmt = "DELETE FROM
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         WHERE
-+                            iss_id IN ($items)";
-+                DB_Helper::getInstance()->query($stmt);
-+            }
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to close off an issue.
-+     *
-+     * @access  public
-+     * @param   integer $usr_id The user ID
-+     * @param   integer $issue_id The issue ID
-+     * @param   bool $send_notification Whether to send a notification about this action or not
-+     * @param   integer $resolution_id The resolution ID
-+     * @param   integer $status_id The status ID
-+     * @param   string $reason The reason for closing this issue
-+     * @param   string  $send_notification_to Who this notification should be sent too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function close($usr_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason, $send_notification_to = 'internal')
-+    {
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $resolution_id = Misc::escapeInteger($resolution_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='closed',
-+                    iss_closed_date='" . Date_Helper::getCurrentDateGMT() . "',\n";
-+        if (!empty($resolution_id)) {
-+            $stmt .= "iss_res_id=$resolution_id,\n";
-+        }
-+        $stmt .= "iss_sta_id=$status_id
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            self::moveOrderForAllUsers($issue_id, 1000);
-+            $prj_id = self::getProjectID($issue_id);
-+
-+            // record the change
-+            History::add($issue_id, $usr_id, History::getTypeID('issue_closed'), "Issue updated to status '" . Status::getStatusTitle($status_id) . "' by " . User::getFullName($usr_id));
-+
-+            if ($send_notification_to == 'all') {
-+
-+                $from = User::getFromHeader($usr_id);
-+                $message_id = User::getFromHeader($usr_id);
-+                $full_email = Support::buildFullHeaders($issue_id, $message_id, $from,
-+                    '', '', 'Issue closed comments', $reason, '');
-+
-+                $structure = Mime_Helper::decode($full_email, true, false);
-+
-+                $email = array(
-+                    'ema_id'        =>  Email_Account::getEmailAccount(self::getProjectID($issue_id)),
-+                    'issue_id'      =>  $issue_id,
-+                    'message_id'    =>  $message_id,
-+                    'date'          =>  Date_Helper::getCurrentDateGMT(),
-+                    'subject'       =>  'Issue closed comments',
-+                    'from'          =>  $from,
-+                    'has_attachment'=>  0,
-+                    'body'          =>  $reason,
-+                    'full_email'    =>  $full_email,
-+                    'headers'       =>  $structure->headers
-+                );
-+                Support::insertEmail($email, $structure, $sup_id, true);
-+                $ids = $sup_id;
-+            } else {
-+                // add note with the reason to close the issue
-+                $_POST['title'] = 'Issue closed comments';
-+                $_POST["note"] = $reason;
-+                Note::insert($usr_id, $issue_id, false, true, true, $send_notification);
-+                $ids = false;
-+            }
-+
-+            if ($send_notification) {
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    // send a special confirmation email when customer issues are closed
-+                    $stmt = "SELECT
-+                                iss_customer_contact_id
-+                             FROM
-+                                " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                             WHERE
-+                                iss_id=$issue_id";
-+                    $customer_contact_id = DB_Helper::getInstance()->getOne($stmt);
-+                    if (!empty($customer_contact_id)) {
-+                        Customer::notifyIssueClosed($prj_id, $issue_id, $customer_contact_id, $send_notification, $resolution_id, $status_id, $reason);
-+                    }
-+                }
-+                // send notifications for the issue being closed
-+                Notification::notify($issue_id, 'closed', $ids);
-+            }
-+            Workflow::handleIssueClosed($prj_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to update the details of a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 or -2 otherwise
-+     */
-+    function update($issue_id)
-+    {
-+        global $errors;
-+        $errors = array();
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $usr_id = Auth::getUserID();
-+        $prj_id = self::getProjectID($issue_id);
-+
-+        $workflow = Workflow::preIssueUpdated($prj_id, $issue_id, $usr_id, $_POST);
-+        if ($workflow !== true) {
-+            return $workflow;
-+        }
-+
-+        // get all of the 'current' information of this issue
-+        $current = self::getDetails($issue_id);
-+        // update the issue associations
-+        if (empty($_POST['associated_issues'])) {
-+            $associated_issues = array();
-+        } else {
-+            $associated_issues = explode(',', @$_POST['associated_issues']);
-+            // make sure all associated issues are valid (and in this project)
-+            for ($i = 0; $i < count($associated_issues); $i++) {
-+                if (!self::exists(trim($associated_issues[$i]), false)) {
-+                    $errors['Associated Issues'][] = 'Issue #' . $associated_issues[$i] . ' does not exist and was removed from the list of associated issues.';
-+                    unset($associated_issues[$i]);
-+                }
-+            }
-+        }
-+        $association_diff = Misc::arrayDiff($current['associated_issues'], $associated_issues);
-+        if (count($association_diff) > 0) {
-+            // go through the new assocations, if association already exists, skip it
-+            $associations_to_remove = $current['associated_issues'];
-+            if (count($associated_issues) > 0) {
-+                foreach ($associated_issues as $index => $associated_id) {
-+                    if (!in_array($associated_id, $current['associated_issues'])) {
-+                        self::addAssociation($issue_id, $associated_id, $usr_id);
-+                    } else {
-+                        // already assigned, remove this user from list of users to remove
-+                        unset($associations_to_remove[array_search($associated_id, $associations_to_remove)]);
-+                    }
-+                }
-+            }
-+            if (count($associations_to_remove) > 0) {
-+                foreach ($associations_to_remove as $associated_id) {
-+                    self::deleteAssociation($issue_id, $associated_id);
-+                }
-+            }
-+        }
-+        $assignments_changed = false;
-+        if (@$_POST["keep_assignments"] == "no") {
-+            // only change the issue-user associations if there really were any changes
-+            $old_assignees = array_merge($current['assigned_users'], $current['assigned_inactive_users']);
-+            if (!empty($_POST['assignments'])) {
-+                $new_assignees = @$_POST['assignments'];
-+            } else {
-+                $new_assignees = array();
-+            }
-+            $assignment_notifications = array();
-+
-+            // remove people from the assignment list, if appropriate
-+            foreach ($old_assignees as $assignee) {
-+                if (!in_array($assignee, $new_assignees)) {
-+                    self::deleteUserAssociation($issue_id, $assignee);
-+                    $assignments_changed = true;
-+                }
-+            }
-+            // add people to the assignment list, if appropriate
-+            foreach ($new_assignees as $assignee) {
-+                if (!in_array($assignee, $old_assignees)) {
-+                    self::addUserAssociation($usr_id, $issue_id, $assignee);
-+                    Notification::subscribeUser($usr_id, $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'issue_update'), TRUE);
-+                    $assignment_notifications[] = $assignee;
-+                    $assignments_changed = true;
-+                }
-+            }
-+            if (count($assignment_notifications) > 0) {
-+                Notification::notifyNewAssignment($assignment_notifications, $issue_id);
-+            }
-+        }
-+        if (empty($_POST["estimated_dev_time"])) {
-+            $_POST["estimated_dev_time"] = 0;
-+        }
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='updated',";
-+        if (!empty($_POST["category"])) {
-+            $stmt .= "iss_prc_id=" . Misc::escapeInteger($_POST["category"]) . ",";
-+        }
-+        if (@$_POST["keep"] == "no") {
-+            $stmt .= "iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",";
-+        }
-+        if (!empty($_POST['expected_resolution_date'])) {
-+            $stmt .= "iss_expected_resolution_date='" . Misc::escapeString($_POST['expected_resolution_date']) . "',";
-+        } else {
-+            $stmt .= "iss_expected_resolution_date=null,";
-+        }
-+        $stmt .= "
-+                    iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",
-+                    iss_pri_id=" . Misc::escapeInteger($_POST["priority"]) . ",
-+                    iss_sta_id=" . Misc::escapeInteger($_POST["status"]) . ",
-+                    iss_res_id=" . Misc::escapeInteger($_POST["resolution"]) . ",
-+                    iss_summary='" . Misc::escapeString($_POST["summary"]) . "',
-+                    iss_description='" . Misc::escapeString($_POST["description"]) . "',
-+                    iss_dev_time='" . Misc::escapeString($_POST["estimated_dev_time"]) . "',
-+                    iss_percent_complete= '" . Misc::escapeString($_POST["percent_complete"]) . "',
-+                    iss_trigger_reminders=" . Misc::escapeInteger($_POST["trigger_reminders"]) . ",
-+                    iss_grp_id ='" . Misc::escapeInteger($_POST["group"]) . "'";
-+        if (isset($_POST['private'])) {
-+            $stmt .= ",
-+                    iss_private = " . Misc::escapeInteger($_POST['private']);
-+        }
-+        $stmt .= "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // add change to the history (only for changes on specific fields?)
-+            $updated_fields = array();
-+            if ($current["iss_expected_resolution_date"] != $_POST['expected_resolution_date']) {
-+                $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $_POST['expected_resolution_date']);
-+            }
-+            if ($current["iss_prc_id"] != $_POST["category"]) {
-+                $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($_POST["category"]));
-+            }
-+            if ($current["iss_pre_id"] != $_POST["release"]) {
-+                $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($_POST["release"]));
-+            }
-+            if ($current["iss_pri_id"] != $_POST["priority"]) {
-+                $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($_POST["priority"]));
-+                Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $_POST);
-+            }
-+            if ($current["iss_sta_id"] != $_POST["status"]) {
-+                // clear out the last-triggered-reminder flag when changing the status of an issue
-+                Reminder_Action::clearLastTriggered($issue_id);
-+
-+                // if old status was closed and new status is not, clear closed data from issue.
-+                $old_status_details = Status::getDetails($current['iss_sta_id']);
-+                if ($old_status_details['sta_is_closed'] == 1) {
-+                    $new_status_details = Status::getDetails($_POST["status"]);
-+                    if ($new_status_details['sta_is_closed'] != 1) {
-+                        self::clearClosed($issue_id);
-+                    }
-+                }
-+                $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($_POST["status"]));
-+            }
-+            if ($current["iss_res_id"] != $_POST["resolution"]) {
-+                $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($_POST["resolution"]));
-+            }
-+            if ($current["iss_dev_time"] != $_POST["estimated_dev_time"]) {
-+                $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($_POST["estimated_dev_time"]*60)));
-+            }
-+            if ($current["iss_summary"] != $_POST["summary"]) {
-+                $updated_fields["Summary"] = '';
-+            }
-+            if ($current["iss_description"] != $_POST["description"]) {
-+                $updated_fields["Description"] = '';
-+            }
-+            if ((isset($_POST['private'])) && ($_POST['private'] != $current['iss_private'])) {
-+                $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($_POST['private']));
-+            }
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $i = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($i > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    if (($key != "Summary") && ($key != "Description")) {
-+                        $changes .= "$key: $value";
-+                    } else {
-+                        $changes .= "$key";
-+                    }
-+                    $i++;
-+                }
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
-+                // send notifications for the issue being updated
-+                Notification::notifyIssueUpdated($issue_id, $current, $_POST);
-+            }
-+
-+            // record group change as a seperate change
-+            if ($current["iss_grp_id"] != (int)$_POST["group"]) {
-+                History::add($issue_id, $usr_id, History::getTypeID('group_changed'),
-+                    "Group changed (" . History::formatChanges(Group::getName($current["iss_grp_id"]), Group::getName($_POST["group"])) . ") by " . User::getFullName($usr_id));
-+            }
-+
-+            // now update any duplicates, if any
-+            $update_dupe = array(
-+                'Category',
-+                'Release',
-+                'Priority',
-+                'Release',
-+                'Resolution'
-+            );
-+            // COMPAT: the following line requires PHP > 4.0.4
-+            $intersect = array_intersect($update_dupe, array_keys($updated_fields));
-+            if (($current["duplicates"] != '') && (count($intersect) > 0)) {
-+                self::updateDuplicates($issue_id);
-+            }
-+
-+            // if there is customer integration, mark last customer action
-+            if ((Customer::hasCustomerIntegration($prj_id)) && (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer'))) {
-+                self::recordLastCustomerAction($issue_id);
-+            }
-+
-+            if ($assignments_changed) {
-+                // XXX: we may want to also send the email notification for those "new" assignees
-+                Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, $usr_id, self::getDetails($issue_id), @$_POST['assignments'], false);
-+            }
-+
-+            Workflow::handleIssueUpdated($prj_id, $issue_id, $usr_id, $current, $_POST);
-+            // Move issue to another project
-+            if (isset($_POST['move_issue']) and (User::getRoleByUser($usr_id, $prj_id) >= User::getRoleID("Developer"))) {
-+                $new_prj_id = (int)@$_POST['new_prj'];
-+                if (($prj_id != $new_prj_id) && (array_key_exists($new_prj_id, Project::getAssocList($usr_id)))) {
-+                    if(User::getRoleByUser($usr_id, $new_prj_id) >= User::getRoleID("Reporter")) {
-+                        $res = self::moveIssue($issue_id, $new_prj_id);
-+                        if ($res == -1) {
-+                            return $res;
-+                        }
-+                    } else {
-+                        return -1;
-+                    }
-+                }
-+            }
-+            return 1;
-+        }
-+    }
-+
-+    /**
-+     * Method used to update the a single detail field of a specific issue.
-+     *
-+     * @param integer $issue_id
-+     * @param string $field_name
-+     * @param string $field_value
-+     * @param string $field_type string or integer (for escape)
-+     * @return integer 1 on success, -1 otherwise
-+     */
-+    function updateField($issue_id, $field_name, $filed_value) {
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $usr_id = Auth::getUserID();
-+        $prj_id = self::getProjectID($issue_id);
-+
-+        // get all of the 'current' information of this issue
-+        $current = self::getDetails($issue_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='updated'";
-+
-+        switch ($field_name) {
-+            case 'category':
-+                $stmt .= ", iss_prc_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'release':
-+                $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'expected_resolution_date':
-+                $stmt .= ", iss_expected_resolution_date = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'release':
-+                $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'priority':
-+                $stmt .= ", iss_pri_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'status':
-+                $stmt .= ", iss_sta_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'resolution':
-+                $stmt .= ", iss_res_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'summary':
-+                $stmt .= ", iss_summary = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'description':
-+                $stmt .= ", iss_description = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'estimated_dev_time':
-+                $stmt .= ", iss_dev_time = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'percent_complete':
-+                $stmt .= ", iss_percent_complete = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'trigger_reminders':
-+                $stmt .= ", iss_trigger_reminders = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'group':
-+                $stmt .= ", iss_grp_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'private':
-+                $stmt .= ", iss_private = " . Misc::escapeInteger($filed_value);
-+            break;
-+            default:
-+                Error_Handler::logError("Unknown field name $field_name", __FILE__, __LINE__);
-+                return -1;
-+            break;
-+        }
-+
-+        $stmt .= "
-+                 WHERE
-+                    iss_id=$issue_id";
-+
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $new = array(
-+                'category' => $current['iss_prc_id'],
-+                'release' => $current['iss_pre_id'],
-+                'expected_resolution_date' => $current['iss_expected_resolution_date'],
-+                'release' => $current['iss_pre_id'],
-+                'priority' => $current['iss_pri_id'],
-+                'status' => $current['iss_sta_id'],
-+                'resolution' => $current['iss_res_id'],
-+                'summary' => $current['iss_summary'],
-+                'description' => $current['iss_description'],
-+                'estimated_dev_time' => $current['iss_dev_time'],
-+                'percent_complete' => $current['iss_percent_complete'],
-+                'trigger_reminders' => $current['iss_trigger_reminders'],
-+                'group' => $current['iss_grp_id'],
-+                'iss_private' => $current['private']
-+            );
-+            $new[$field_name] = $filed_value;
-+
-+            // add change to the history (only for changes on specific fields?)
-+            $updated_fields = array();
-+            if ($field_name == 'expected_resolution_date' && $current["iss_expected_resolution_date"] != $filed_value) {
-+                $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $filed_value);
-+            }
-+            if ($field_name == 'category' && $current["iss_prc_id"] != $filed_value) {
-+                $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($filed_value));
-+            }
-+            if ($field_name == 'release' && $current["iss_pre_id"] != $filed_value) {
-+                $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($filed_value));
-+            }
-+            if ($field_name == 'priority' && $current["iss_pri_id"] != $filed_value) {
-+                $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($filed_value));
-+                Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $new);
-+            }
-+            if ($field_name == 'status' && $current["iss_sta_id"] != $filed_value) {
-+                // clear out the last-triggered-reminder flag when changing the status of an issue
-+                Reminder_Action::clearLastTriggered($issue_id);
-+
-+                // if old status was closed and new status is not, clear closed data from issue.
-+                $old_status_details = Status::getDetails($current['iss_sta_id']);
-+                if ($old_status_details['sta_is_closed'] == 1) {
-+                    $new_status_details = Status::getDetails($filed_value);
-+                    if ($new_status_details['sta_is_closed'] != 1) {
-+                        self::clearClosed($issue_id);
-+                    }
-+                }
-+                $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($filed_value));
-+            }
-+            if ($field_name == 'resolution' && $current["iss_res_id"] != $filed_value) {
-+                $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($filed_value));
-+            }
-+            if ($field_name == 'estimated_dev_time' && $current["iss_dev_time"] != $filed_value) {
-+                $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($filed_value*60)));
-+            }
-+            if ($field_name == 'summary' && $current["iss_summary"] != $filed_value) {
-+                $updated_fields["Summary"] = '';
-+            }
-+            if ($field_name == 'description' && $current["iss_description"] != $filed_value) {
-+                $updated_fields["Description"] = '';
-+            }
-+            if ($field_name == 'private' && ($filed_value != $current['iss_private'])) {
-+                $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($filed_value));
-+            }
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $i = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($i > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    if (($key != "Summary") && ($key != "Description")) {
-+                        $changes .= "$key: $value";
-+                    } else {
-+                        $changes .= "$key";
-+                    }
-+                    $i++;
-+                }
-+
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
-+                // send notifications for the issue being updated
-+                Notification::notifyIssueUpdated($issue_id, $current, $new);
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Move the issue to a new project
-+     *
-+     * @param integer $issue_id
-+     * @param integer $new_prj_id
-+     * @return integer 1 on success, -1 otherwise
-+     */
-+    function moveIssue($issue_id, $new_prj_id)
-+    {
-+        $stmt = "UPDATE
-+              " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+          SET
-+              iss_prj_id = " . Misc::escapeInteger($new_prj_id) . "
-+          WHERE
-+              iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $currentDetails = self::getDetails($issue_id);
-+
-+            // set new category
-+            $new_iss_prc_list = Category::getAssocList($new_prj_id);
-+            $iss_prc_title = Category::getTitle($currentDetails['iss_prc_id']);
-+            $new_prc_id = array_search($iss_prc_title, $new_iss_prc_list);
-+            if ($new_prc_id === false) {
-+              // use the first category listed in the new project
-+              $new_prc_id = key($new_iss_prc_list);
-+            }
-+
-+            // set new priority
-+            $new_iss_pri_list = Priority::getAssocList($new_prj_id);
-+            $iss_pri_title = Priority::getTitle($currentDetails['iss_pri_id']);
-+            $new_pri_id = array_search($iss_pri_title, $new_iss_pri_list);
-+            if ($new_pri_id === false) {
-+              // use the first category listed in the new project
-+              $new_pri_id = key($new_iss_pri_list);
-+            }
-+
-+            // XXX: Set status if needed when moving issue
-+
-+            $stmt = "UPDATE
-+                  " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+              SET
-+                  iss_prc_id=" . Misc::escapeInteger($new_prc_id) . ",
-+                  iss_pri_id=" . $new_pri_id . "
-+              WHERE
-+                  iss_id=$issue_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            }
-+
-+            // clear project cache
-+            self::getProjectID($issue_id, true);
-+
-+            Notification::notifyNewIssue($new_prj_id, $issue_id);
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to associate an existing issue with another one.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $issue_id The other issue ID
-+     * @return  void
-+     */
-+    function addAssociation($issue_id, $associated_id, $usr_id, $link_issues = TRUE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $associated_id = Misc::escapeInteger($associated_id);
-+
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 (
-+                    isa_issue_id,
-+                    isa_associated_id
-+                 ) VALUES (
-+                    $issue_id,
-+                    $associated_id
-+                 )";
-+        DB_Helper::getInstance()->query($stmt);
-+        History::add($issue_id, $usr_id, History::getTypeID('issue_associated'), "Issue associated to #$associated_id by " . User::getFullName($usr_id));
-+        // link the associated issue back to this one
-+        if ($link_issues) {
-+            self::addAssociation($associated_id, $issue_id, $usr_id, FALSE);
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove the issue associations related to a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  void
-+     */
-+    function deleteAssociations($issue_id, $usr_id = FALSE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (is_array($issue_id)) {
-+            $issue_id = implode(", ", $issue_id);
-+        }
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 WHERE
-+                    isa_issue_id IN ($issue_id) OR
-+                    isa_associated_id IN ($issue_id)";
-+        DB_Helper::getInstance()->query($stmt);
-+        if ($usr_id) {
-+            History::add($issue_id, $usr_id, History::getTypeID('issue_all_unassociated'), 'Issue associations removed by ' . User::getFullName($usr_id));
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove a issue association from an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $associated_id The associated issue ID to remove.
-+     * @return  void
-+     */
-+    function deleteAssociation($issue_id, $associated_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $associated_id = Misc::escapeInteger($associated_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 WHERE
-+                    (
-+                        isa_issue_id = $issue_id AND
-+                        isa_associated_id = $associated_id
-+                    ) OR
-+                    (
-+                        isa_issue_id = $associated_id AND
-+                        isa_associated_id = $issue_id
-+                    )";
-+        DB_Helper::getInstance()->query($stmt);
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_unassociated'),
-+                "Issue association #$associated_id removed by " . User::getFullName(Auth::getUserID()));
-+        History::add($associated_id, Auth::getUserID(), History::getTypeID('issue_unassociated'),
-+                "Issue association #$issue_id removed by " . User::getFullName(Auth::getUserID()));
-+    }
-+
-+
-+    /**
-+     * Method used to assign an issue with an user.
-+     *
-+     * @access  public
-+     * @param   integer $usr_id The user ID of the person performing this change
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $assignee_usr_id The user ID of the assignee
-+     * @param   boolean $add_history Whether to add a history entry about this or not
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function addUserAssociation($usr_id, $issue_id, $assignee_usr_id, $add_history = TRUE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $assignee_usr_id = Misc::escapeInteger($assignee_usr_id);
-+        $order = 1;
-+        // move all orders down to free "order space" for this new association
-+        $stmt = "UPDATE 
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 SET
-+                    isu_order = isu_order + 1
-+                 WHERE
-+                    isu_usr_id = $assignee_usr_id AND
-+                    isu_order >= $order";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        // insert the new association
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 (
-+                    isu_iss_id,
-+                    isu_usr_id,
-+                    isu_assigned_date,
-+                    isu_order
-+                 ) VALUES (
-+                    $issue_id,
-+                    $assignee_usr_id,
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    $order
-+                 )";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($add_history) {
-+                History::add($issue_id, $usr_id, History::getTypeID('user_associated'),
-+                    'Issue assigned to ' . User::getFullName($assignee_usr_id) . ' by ' . User::getFullName($usr_id));
-+            }
-+            return 1;
-+        }
-+    }
-+
-+    /**
-+     * Method used to get the order list to be rearranged
-+     * 
-+     * @access  private
-+     * @param   string $issue_id The issue ID or a comma seperated list of IDs already prepared for giving to mysql
-+     * @param   string $usr_id The user to remove. When not specified, all users are taken as to be removed for that issue
-+     * @return  mixed delete order list to be rearranged. Used as a parameter to the method of rearranging the order.
-+     */
-+    function getDeleteUserAssociationOrderList($issue_id, $usr_id = "")
-+    {
-+        // find all affected associantion orders
-+        $stmt = "SELECT isu_usr_id, isu_order FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                 isu_iss_id IN ($issue_id)";
-+        if ($usr_id !== FALSE) {
-+            $stmt.= " AND isu_usr_id IN ($usr_id)";
-+        }
-+        $stmt.= "ORDER BY isu_order";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $deleted_orders = array();
-+            foreach ($res as $row) {
-+                if (empty($deleted_orders[$row['isu_usr_id']])) {
-+                    $deleted_orders[$row['isu_usr_id']] = array();
-+                }
-+                $deleted_orders[$row['isu_usr_id']] [] = $row['isu_order'];
-+            }
-+            return $deleted_orders;
-+        }
-+    }
-+
-+    /**
-+     *
-+     * Method used to rearrange order list in the db according to known deleted records
-+     *
-+     * @access  private
-+     * @param   mixed  deleteorder list
-+     * @return void
-+     */
-+    function rearrangeDeleteUserAssociationOrderList($delete_order_list)
-+    {
-+        if (empty($delete_order_list) || (!is_array($delete_order_list))) {
-+            return -1;
-+        }
-+        foreach ($delete_order_list as $isu_usr_id => $orders) {
-+            for ($i = 0; $i < count($orders); $i++) { // traverse all deleted orders
-+                // move the orders after them up to take the "order space" of the deleted records
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                         SET
-+                            isu_order = isu_order - " . ($i+1) . "
-+                         WHERE
-+                            isu_usr_id = $isu_usr_id AND
-+                            isu_order > " . $orders[$i];
-+                if ($i < count($orders) - 1) {
-+                    $stmt.=  " AND
-+                            isu_order < " . $orders[$i+1];
-+                }
-+                $res = DB_Helper::getInstance()->query($stmt);
-+                if (PEAR::isError($res)) {
-+                    Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                    return -1;
-+                }
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Method used to delete all user assignments for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing the change
-+     * @return  void
-+     */
-+    function deleteUserAssociations($issue_id, $usr_id = FALSE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (is_array($issue_id)) {
-+            $issue_id = implode(", ", $issue_id);
-+        }
-+        $deleted_order_list = self::getDeleteUserAssociationOrderList($issue_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id IN ($issue_id)";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($usr_id) {
-+                History::add($issue_id, $usr_id, History::getTypeID('user_all_unassociated'), 'Issue assignments removed by ' . User::getFullName($usr_id));
-+            }
-+            self::rearrangeDeleteUserAsssociationOrderList($deleted_order_list);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to delete a single user assignments for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user to remove.
-+     * @param   boolean $add_history Whether to add a history entry about this or not
-+     * @return  void
-+     */
-+    function deleteUserAssociation($issue_id, $usr_id, $add_history = true)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $delete_order_list = self::getDeleteUserAssociationOrderList($issue_id, $usr_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id = $issue_id AND
-+                    isu_usr_id = $usr_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($add_history) {
-+                History::add($issue_id, Auth::getUserID(), History::getTypeID('user_unassociated'),
-+                    User::getFullName($usr_id) . ' removed from issue by ' . User::getFullName(Auth::getUserID()));
-+            }
-+            self::rearrangeDeleteUserAssociationOrderList($delete_order_list);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Creates an issue with the given email information.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @param   integer $usr_id The user responsible for this action
-+     * @param   string $sender The original sender of this email
-+     * @param   string $summary The issue summary
-+     * @param   string $description The issue description
-+     * @param   integer $category The category ID
-+     * @param   integer $priority The priority ID
-+     * @param   array $assignment The list of users to assign this issue to
-+     * @param   string $date The date the email was originally sent.
-+     * @param   string $msg_id The message ID of the email we are creating this issue from.
-+     * @return  void
-+     */
-+    function createFromEmail($prj_id, $usr_id, $sender, $summary, $description, $category, $priority, $assignment, $date, $msg_id)
-+    {
-+        $data = array();
-+        $exclude_list = array();
-+
-+        $sender_email = Mail_Helper::getEmailAddress($sender);
-+        $sender_usr_id = User::getUserIDByEmail($sender_email, true);
-+        if (!empty($sender_usr_id)) {
-+            $reporter = $sender_usr_id;
-+            $exclude_list[] = $sender_usr_id;
-+        }
-+
-+        $data = array(
-+            'category' => $category,
-+            'priority' => $priority,
-+            'description' => $description,
-+            'summary' => $summary,
-+            'msg_id' => $msg_id,
-+        );
-+
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            list($customer_id, $customer_contact_id) = Customer::getCustomerIDByEmails($prj_id, array($sender_email));
-+            if (!empty($customer_id)) {
-+                $contact = Customer::getContactDetails($prj_id, $customer_contact_id);
-+                // overwrite the reporter with the customer contact
-+                $reporter = User::getUserIDByContactID($customer_contact_id);
-+                $contact_timezone = Date_Helper::getPreferredTimezone($reporter);
-+
-+                $data['customer'] = $customer_id;
-+                $data['contact'] = $customer_contact_id;
-+#                $data['contract'] =  // XXX missing
-+                $data['contact_person_lname'] = $contact['last_name'];
-+                $data['contact_person_fname'] = $contact['first_name'];
-+                $data['contact_email'] = $sender_email;
-+                $data['contact_phone'] = $contact['phone'];
-+                $data['contact_timezone'] = $contact_timezone;
-+            }
-+        } else {
-+            $customer_id = FALSE;
-+        }
-+        if (empty($reporter)) {
-+            $reporter = APP_SYSTEM_USER_ID;
-+        }
-+
-+        $data['reporter'] = $reporter;
-+
-+        $issue_id = self::insertIssue($prj_id, $usr_id, $data);
-+        if ($issue_id == -1) {
-+            return -1;
-+        }
-+
-+        $has_TAM = false;
-+        $has_RR = false;
-+        // log the creation of the issue
-+        History::add($issue_id, $usr_id, History::getTypeID('issue_opened'), 'Issue opened by ' . $sender);
-+
-+        $emails = array();
-+        $manager_usr_ids = array();
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (!empty($customer_id))) {
-+            // if there are any technical account managers associated with this customer, add these users to the notification list
-+            $managers = Customer::getAccountManagers($prj_id, $customer_id);
-+            $manager_usr_ids = array_keys($managers);
-+            $manager_emails = array_values($managers);
-+            $emails = array_merge($emails, $manager_emails);
-+        }
-+        // add the reporter to the notification list
-+        $emails[] = $sender;
-+        $emails = array_unique($emails);
-+        $actions = Notification::getDefaultActions($issue_id, false, 'issue_from_email');
-+        foreach ($emails as $address) {
-+            Notification::subscribeEmail($reporter, $issue_id, $address, $actions);
-+        }
-+
-+        // only assign the issue to an user if the associated customer has any technical account managers
-+        $users = array();
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (count($manager_usr_ids) > 0)) {
-+            foreach ($manager_usr_ids as $manager_usr_id) {
-+                $users[] = $manager_usr_id;
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $manager_usr_id, false);
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)');
-+            }
-+            $has_TAM = true;
-+        }
-+        // now add the user/issue association
-+        if (@count($assignment) > 0) {
-+            for ($i = 0; $i < count($assignment); $i++) {
-+                Notification::subscribeUser($reporter, $issue_id, $assignment[$i], $actions);
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $assignment[$i]);
-+                if ($assignment[$i] != $usr_id) {
-+                    $users[] = $assignment[$i];
-+                }
-+            }
-+        } else {
-+            // only use the round-robin feature if this new issue was not
-+            // already assigned to a customer account manager
-+            if (@count($manager_usr_ids) < 1) {
-+                $assignee = Round_Robin::getNextAssignee($prj_id);
-+                // assign the issue to the round robin person
-+                if (!empty($assignee)) {
-+                    self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $assignee, false);
-+                    History::add($issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)');
-+                    $users[] = $assignee;
-+                    $has_RR = true;
-+                }
-+            }
-+        }
-+        if (count($users) > 0) {
-+            $has_assignee = true;
-+        }
-+
-+        Workflow::handleNewIssue($prj_id, $issue_id, $has_TAM, $has_RR);
-+
-+        // send special 'an issue was auto-created for you' notification back to the sender
-+        Notification::notifyAutoCreatedIssue($prj_id, $issue_id, $sender, $date, $summary);
-+
-+        // also notify any users that want to receive emails anytime a new issue is created
-+        Notification::notifyNewIssue($prj_id, $issue_id, $exclude_list);
-+
-+        return $issue_id;
-+    }
-+
-+
-+    /**
-+     * Return errors that happened when creating new issue from POST method.
-+     *
-+     * @return  array
-+     */
-+    private static $insert_errors = array();
-+    static function getInsertErrors() {
-+        return self::$insert_errors;
-+    }
-+
-+    /**
-+     * Method used to add a new issue using the normal report form.
-+     *
-+     * @access  public
-+     * @return  integer The new issue ID
-+     */
-+    function createFromPost()
-+    {
-+        $keys = array(
-+            'add_primary_contact', 'attached_emails', 'category', 'contact', 'contact_email', 'contact_extra_emails', 'contact_person_fname',
-+            'contact_person_lname', 'contact_phone', 'contact_timezone', 'contract', 'customer', 'custom_fields', 'description',
-+            'estimated_dev_time', 'group', 'notify_customer', 'notify_senders', 'priority', 'private', 'release', 'summary', 'users',
-+        );
-+        $data = array();
-+        foreach ($keys as $key) {
-+            if (isset($_POST[$key])) {
-+                $data[$key] = $_POST[$key];
-+            }
-+        }
-+
-+        $prj_id = Auth::getCurrentProject();
-+        $usr_id = Auth::getUserID();
-+
-+        // if we are creating an issue for a customer, put the
-+        // main customer contact as the reporter for it
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $contact_usr_id = User::getUserIDByContactID($data['contact']);
-+            if (empty($contact_usr_id)) {
-+                $contact_usr_id = $usr_id;
-+            }
-+            $data['reporter'] = $contact_usr_id;
-+        } else {
-+            $data['reporter'] = $usr_id;
-+        }
-+
-+        $data['msg_id'] = Mail_Helper::generateMessageID();
-+
-+        $issue_id = self::insertIssue($prj_id, $usr_id, $data);
-+        if ($issue_id == -1) {
-+            return -1;
-+        }
-+
-+        $has_TAM = false;
-+        $has_RR = false;
-+        $info = User::getNameEmail($usr_id);
-+        // log the creation of the issue
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_opened'), 'Issue opened by ' . User::getFullName(Auth::getUserID()));
-+
-+        $emails = array();
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            if (!empty($data['contact_extra_emails']) && count($data['contact_extra_emails']) > 0) {
-+                $emails = $data['contact_extra_emails'];
-+            }
-+            // add the primary contact to the notification list
-+            if ($data['add_primary_contact'] == 'yes') {
-+                $contact_email = User::getEmailByContactID($data['contact']);
-+                if (!empty($contact_email)) {
-+                    $emails[] = $contact_email;
-+                }
-+            }
-+            // if there are any technical account managers associated with this customer, add these users to the notification list
-+            $managers = Customer::getAccountManagers($prj_id, $data['customer']);
-+            $manager_usr_ids = array_keys($managers);
-+            $manager_emails = array_values($managers);
-+            $emails = array_merge($emails, $manager_emails);
-+        }
-+        // add the reporter to the notification list
-+        $emails[] = $info['usr_email'];
-+        $emails = array_unique($emails);
-+        foreach ($emails as $address) {
-+            Notification::subscribeEmail($usr_id, $issue_id, $address, Notification::getDefaultActions($issue_id, $address, 'new_issue'));
-+        }
-+
-+        // only assign the issue to an user if the associated customer has any technical account managers
-+        $users = array();
-+        $has_TAM = false;
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (count($manager_usr_ids) > 0)) {
-+            foreach ($manager_usr_ids as $manager_usr_id) {
-+                $users[] = $manager_usr_id;
-+                self::addUserAssociation($usr_id, $issue_id, $manager_usr_id, false);
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)');
-+            }
-+            $has_TAM = true;
-+        }
-+        // now add the user/issue association (aka assignments)
-+        if (!empty($data['users']) && count($data['users']) > 0) {
-+            for ($i = 0; $i < count($data['users']); $i++) {
-+                Notification::subscribeUser($usr_id, $issue_id, $data['users'][$i],
-+                                Notification::getDefaultActions($issue_id, User::getEmail($data['users'][$i]), 'new_issue'));
-+                self::addUserAssociation($usr_id, $issue_id, $data['users'][$i]);
-+                if ($data['users'][$i] != $usr_id) {
-+                    $users[] = $data['users'][$i];
-+                }
-+            }
-+        } else {
-+            // only use the round-robin feature if this new issue was not
-+            // already assigned to a customer account manager
-+            if (@count($manager_usr_ids) < 1) {
-+                $assignee = Round_Robin::getNextAssignee($prj_id);
-+                // assign the issue to the round robin person
-+                if (!empty($assignee)) {
-+                    $users[] = $assignee;
-+                    self::addUserAssociation($usr_id, $issue_id, $assignee, false);
-+                    History::add($issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)');
-+                    $has_RR = true;
-+                }
-+            }
-+        }
-+
-+        // now process any files being uploaded
-+        $found = 0;
-+        for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+            if (!@empty($_FILES["file"]["name"][$i])) {
-+                $found = 1;
-+                break;
-+            }
-+        }
-+        if ($found) {
-+            $files = array();
-+            for ($i = 0; $i < count($_FILES["file"]["name"]); $i++) {
-+                $filename = @$_FILES["file"]["name"][$i];
-+                if (empty($filename)) {
-+                    continue;
-+                }
-+                $blob = file_get_contents($_FILES["file"]["tmp_name"][$i]);
-+                if (empty($blob)) {
-+                    // error reading a file
-+                    self::$insert_errors["file[$i]"] = "There was an error uploading the file '$filename'.";
-+                    continue;
-+                }
-+                $files[] = array(
-+                    "filename" => $filename,
-+                    "type"     => $_FILES['file']['type'][$i],
-+                    "blob"     => $blob
-+                );
-+            }
-+            if (count($files) > 0) {
-+                $attachment_id = Attachment::add($issue_id, $usr_id, 'Files uploaded at issue creation time');
-+                foreach ($files as $file) {
-+                    Attachment::addFile($attachment_id, $file["filename"], $file["type"], $file["blob"]);
-+                }
-+            }
-+        }
-+        // need to associate any emails ?
-+        if (!empty($data['attached_emails'])) {
-+            $items = explode(",", $data['attached_emails']);
-+            Support::associate($usr_id, $issue_id, $items);
-+        }
-+        // need to notify any emails being converted into issues ?
-+        if (@count($data['notify_senders']) > 0) {
-+            $recipients = Notification::notifyEmailConvertedIntoIssue($prj_id, $issue_id, $data['notify_senders'], @$data['customer']);
-+        } else {
-+            $recipients = array();
-+        }
-+        // need to process any custom fields ?
-+        if (@count($data['custom_fields']) > 0) {
-+            foreach ($data['custom_fields'] as $fld_id => $value) {
-+                Custom_Field::associateIssue($issue_id, $fld_id, $value);
-+            }
-+        }
-+        // also send a special confirmation email to the customer contact
-+        if ((@$data['notify_customer'] == 'yes') && (!empty($data['contact']))) {
-+            // also need to pass the list of sender emails already notified,
-+            // so we can avoid notifying the same person again
-+            $contact_email = User::getEmailByContactID($data['contact']);
-+            if (@!in_array($contact_email, $recipients)) {
-+                Customer::notifyCustomerIssue($prj_id, $issue_id, $data['contact']);
-+            }
-+        }
-+
-+        Workflow::handleNewIssue($prj_id, $issue_id, $has_TAM, $has_RR);
-+
-+        // also notify any users that want to receive emails anytime a new issue is created
-+        Notification::notifyNewIssue($prj_id, $issue_id);
-+
-+        return $issue_id;
-+    }
-+
-+    /**
-+     * Insert issue to database.
-+     *
-+     * @param   integer $prj_id The project ID
-+     * @param   integer $usr_id The user responsible for this action
-+     * @param   array $data of issue to be inserted
-+     * @return  integer The new issue ID
-+     */
-+    private function insertIssue($prj_id, $usr_id, $data)
-+    {
-+
-+        // XXX missing_fields never used
-+        $missing_fields = array();
-+        if ($data['category'] == -1) {
-+            $missing_fields[] = 'Category';
-+        }
-+        if ($data['priority'] == -1) {
-+            $missing_fields[] = 'Priority';
-+        }
-+
-+        // if there is no reporter set, use the system user
-+        if (empty($data['reporter'])) {
-+              $data['reporter'] = APP_SYSTEM_USER_ID;
-+        }
-+
-+        if ((!isset($data['estimated_dev_time'])) || ($data['estimated_dev_time'] == '')) {
-+            $data['estimated_dev_time'] = 0;
-+        }
-+
-+        // add new issue
-+        $stmt = "INSERT INTO " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue ".
-+                "SET ".
-+                    "iss_prj_id=" . $prj_id . ",";
-+        if (!empty($data['group'])) {
-+            $stmt .= "iss_grp_id=" . Misc::escapeInteger($data['group']) . ",\n";
-+        }
-+        if (!empty($data['category'])) {
-+            $stmt .= "iss_prc_id=". Misc::escapeInteger($data['category']) . ",\n";
-+        }
-+        if (!empty($data['release'])) {
-+            $stmt .= "iss_pre_id=". Misc::escapeInteger($data['release']) . ",\n";
-+        }
-+        if (!empty($data['priority'])) {
-+            $stmt .= "iss_pri_id=". Misc::escapeInteger($data['priority']) . ",";
-+        }
-+
-+        $stmt .= "iss_usr_id=". Misc::escapeInteger($data['reporter']) .",";
-+
-+        $initial_status = Project::getInitialStatus($prj_id);
-+        if (!empty($initial_status)) {
-+            $stmt .= "iss_sta_id=" . Misc::escapeInteger($initial_status) . ",";
-+        }
-+
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $stmt .= "
-+                    iss_customer_id=". Misc::escapeInteger($data['customer']) . ",";
-+            if (!empty($data['contact'])) {
-+            $stmt .= "
-+                    iss_customer_contract_id='". Misc::escapeString($data['contract']) . "',";
-+            }
-+            $stmt .= "
-+                    iss_customer_contact_id=". Misc::escapeInteger($data['contact']) . ",
-+                    iss_contact_person_lname='". Misc::escapeString($data['contact_person_lname']) . "',
-+                    iss_contact_person_fname='". Misc::escapeString($data['contact_person_fname']) . "',
-+                    iss_contact_email='". Misc::escapeString($data['contact_email']) . "',
-+                    iss_contact_phone='". Misc::escapeString($data['contact_phone']) . "',
-+                    iss_contact_timezone='". Misc::escapeString($data['contact_timezone']) . "',";
-+        }
-+
-+        $stmt .= "
-+                    iss_created_date='". Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='created',
-+                    iss_summary='" . Misc::escapeString($data['summary']) . "',
-+                    iss_description='" . Misc::escapeString($data['description']) . "',
-+                    iss_dev_time='" . Misc::escapeString($data['estimated_dev_time']) . "',";
-+            if (!empty($data['contact'])) {
-+                $stmt .= "
-+                    iss_private=" . Misc::escapeInteger($data['private']) . " ,";
-+            }
-+        $stmt .= "
-+                    iss_root_message_id='". Misc::escapeString($data['msg_id']) ."'
-+        ";
-+
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+
-+        $issue_id = DB_Helper::get_last_insert_id();
-+        return $issue_id;
-+    }
-+
-+
-+    /**
-+     * Method used to get a specific parameter in the issue listing cookie.
-+     *
-+     * @access  public
-+     * @param   string $name The name of the parameter
-+     * @return  mixed The value of the specified parameter
-+     */
-+    function getParam($name)
-+    {
-+        $profile = Search_Profile::getProfile(Auth::getUserID(), Auth::getCurrentProject(), 'issue');
-+
-+        if (isset($_GET[$name])) {
-+            return $_GET[$name];
-+        } elseif (isset($_POST[$name])) {
-+            return $_POST[$name];
-+        } elseif (isset($profile[$name])) {
-+            return $profile[$name];
-+        } else {
-+            return "";
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to save the current search parameters in a cookie.
-+     *
-+     * @access  public
-+     * @return  array The search parameters
-+     */
-+    function saveSearchParams()
-+    {
-+        $sort_by = self::getParam('sort_by');
-+        $sort_order = self::getParam('sort_order');
-+        $users = self::getParam('users');
-+        if (empty($users) && ($sort_by == 'isu_order')) { // Sorting by isu_order is impossible when no user specified
-+            unset($sort_by);
-+            unset($sort_order);
-+        }
-+        $rows = self::getParam('rows');
-+        $hide_closed = self::getParam('hide_closed');
-+        if ($hide_closed === '') {
-+            $hide_closed = 1;
-+        }
-+        $search_type = self::getParam('search_type');
-+        if (empty($search_type)) {
-+            $search_type = 'all_text';
-+        }
-+        $custom_field = self::getParam('custom_field');
-+        if (is_string($custom_field)) {
-+            $custom_field = unserialize(urldecode($custom_field));
-+        }
-+        $cookie = array(
-+            'rows'           => $rows ? $rows : APP_DEFAULT_PAGER_SIZE,
-+            'pagerRow'       => self::getParam('pagerRow'),
-+            'hide_closed'    => $hide_closed,
-+            "sort_by"        => $sort_by ? $sort_by : "pri_rank",
-+            "sort_order"     => $sort_order ? $sort_order : "ASC",
-+            // quick filter form
-+            'keywords'       => self::getParam('keywords'),
-+            'search_type'    => $search_type,
-+            'users'          => self::getParam('users'),
-+            'status'         => self::getParam('status'),
-+            'priority'       => self::getParam('priority'),
-+            'category'       => self::getParam('category'),
-+            'customer_email' => self::getParam('customer_email'),
-+            // advanced search form
-+            'show_authorized_issues'        => self::getParam('show_authorized_issues'),
-+            'show_notification_list_issues' => self::getParam('show_notification_list_issues'),
-+            'reporter'       => self::getParam('reporter'),
-+            // other fields
-+            'release'        => self::getParam('release'),
-+            // custom fields
-+            'custom_field'   => $custom_field
-+        );
-+        // now do some magic to properly format the date fields
-+        $date_fields = array(
-+            'created_date',
-+            'updated_date',
-+            'last_response_date',
-+            'first_response_date',
-+            'closed_date'
-+        );
-+        foreach ($date_fields as $field_name) {
-+            $field = self::getParam($field_name);
-+            if (empty($field)) {
-+                continue;
-+            }
-+            if (@$field['filter_type'] == 'in_past') {
-+                @$cookie[$field_name] = array(
-+                    'filter_type'   =>  'in_past',
-+                    'time_period'   =>  $field['time_period']
-+                );
-+            } else {
-+                $end_field_name = $field_name . '_end';
-+                $end_field = self::getParam($end_field_name);
-+                @$cookie[$field_name] = array(
-+                    'past_hour'   => $field['past_hour'],
-+                    'Year'        => $field['Year'],
-+                    'Month'       => $field['Month'],
-+                    'Day'         => $field['Day'],
-+                    'start'       => $field['Year'] . '-' . $field['Month'] . '-' . $field['Day'],
-+                    'filter_type' => $field['filter_type'],
-+                    'end'         => $end_field['Year'] . '-' . $end_field['Month'] . '-' . $end_field['Day']
-+                );
-+                @$cookie[$end_field_name] = array(
-+                    'Year'        => $end_field['Year'],
-+                    'Month'       => $end_field['Month'],
-+                    'Day'         => $end_field['Day']
-+                );
-+            }
-+        }
-+        Search_Profile::save(Auth::getUserID(), Auth::getCurrentProject(), 'issue', $cookie);
-+        return $cookie;
-+    }
-+
-+
-+    /**
-+     * Method used to get the current sorting options used in the grid layout
-+     * of the issue listing page.
-+     *
-+     * @access  public
-+     * @param   array $options The current search parameters
-+     * @return  array The sorting options
-+     */
-+    function getSortingInfo($options)
-+    {
-+
-+        $custom_fields = Custom_Field::getFieldsToBeListed(Auth::getCurrentProject());
-+
-+        // default order for last action date, priority should be descending
-+        // for textual fields, like summary, ascending is reasonable
-+        $fields = array(
-+            "pri_rank" => "desc",
-+            "iss_id" => "desc",
-+            "iss_customer_id" => "desc",
-+            "prc_title" => "asc",
-+            "sta_rank" => "asc",
-+            "iss_created_date" => "desc",
-+            "iss_summary" => "asc",
-+            "last_action_date" => "desc",
-+            "usr_full_name" => "asc",
-+            "iss_expected_resolution_date" => "desc",
-+            "pre_title" => "asc",
-+            "assigned" => "asc",
-+            "isu_order" => "desc",
-+        );
-+
-+        foreach ($custom_fields as $fld_id => $fld_name) {
-+            $fields['custom_field_' . $fld_id] = "desc";
-+        }
-+
-+        $sortfields = array_combine(array_keys($fields), array_keys($fields));
-+        $sortfields["pre_title"] = "pre_scheduled_date";
-+        $sortfields["assigned"] = "isu_usr_id";
-+
-+        $items = array(
-+            "links"  => array(),
-+            "images" => array()
-+        );
-+        foreach ($sortfields as $field => $sortfield) {
-+            $sort_order = $fields[$field];
-+            if ($options["sort_by"] == $sortfield) {
-+                $items["images"][$field] = "images/" . strtolower($options["sort_order"]) . ".gif";
-+                if (strtolower($options["sort_order"]) == "asc") {
-+                    $sort_order = "desc";
-+                } else {
-+                    $sort_order = "asc";
-+                }
-+            }
-+            $items["links"][$field] = $_SERVER["PHP_SELF"] . "?sort_by=" . $sortfield . "&sort_order=" . $sort_order;
-+        }
-+        return $items;
-+    }
-+
-+
-+    /**
-+     * Returns the list of action date fields appropriate for the
-+     * current user ID.
-+     *
-+     * @access  public
-+     * @return  array The list of action date fields
-+     */
-+    function getLastActionFields()
-+    {
-+        $last_action_fields = array(
-+            "iss_last_public_action_date"
-+        );
-+        if (Auth::getCurrentRole() > User::getRoleID('Customer')) {
-+            $last_action_fields[] = "iss_last_internal_action_date";
-+        }
-+        if (count($last_action_fields) > 1) {
-+            return "GREATEST(" . implode(', IFNULL(', $last_action_fields) . ", '0000-00-00')) AS last_action_date";
-+        } else {
-+            return $last_action_fields[0] . " AS last_action_date";
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues to be displayed in the grid layout.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The current project ID
-+     * @param   array $options The search parameters
-+     * @param   integer $current_row The current page number
-+     * @param   integer $max The maximum number of rows per page
-+     * @return  array The list of issues to be displayed
-+     */
-+    function getListing($prj_id, $options, $current_row = 0, $max = 5)
-+    {
-+        if (strtoupper($max) == "ALL") {
-+            $max = 9999999;
-+        }
-+        $start = $current_row * $max;
-+        // get the current user's role
-+        $usr_id = Auth::getUserID();
-+        $role_id = User::getRoleByUser($usr_id, $prj_id);
-+
-+        // get any custom fields that should be displayed
-+        $custom_fields = Custom_Field::getFieldsToBeListed($prj_id);
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_grp_id,
-+                    iss_prj_id,
-+                    iss_sta_id,
-+                    iss_customer_id,
-+                    iss_customer_contract_id,
-+                    iss_created_date,
-+                    iss_updated_date,
-+                    iss_last_response_date,
-+                    iss_closed_date,
-+                    iss_last_customer_action_date,
-+                    iss_usr_id,
-+                    iss_summary,
-+                    pri_title,
-+                    prc_title,
-+                    sta_title,
-+                    sta_color status_color,
-+                    sta_id,
-+                    iqu_status,
-+                    grp_name `group`,
-+                    pre_title,
-+                    iss_last_public_action_date,
-+                    iss_last_public_action_type,
-+                    iss_last_internal_action_date,
-+                    iss_last_internal_action_type,
-+                    " . self::getLastActionFields() . ",
-+                    IF(iss_last_internal_action_date > iss_last_public_action_date, 'internal', 'public') AS action_type,
-+                    iss_private,
-+                    usr_full_name,
-+                    iss_percent_complete,
-+                    iss_dev_time,
-+                    iss_expected_resolution_date
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user";
-+        // join custom fields if we are searching by custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                if (($field['fld_type'] == 'date') && ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . '_' . $cfo_id . "\n";
-+                    }
-+                } else {
-+                    $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . "\n";
-+                }
-+            }
-+        }
-+        $stmt .= ")";
-+        // check for the custom fields we want to sort by
-+        if (strstr($options['sort_by'], 'custom_field') !== false) {
-+            $fld_id = str_replace("custom_field_", '', $options['sort_by']);
-+            $stmt .= "\n LEFT JOIN \n" .
-+                APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf_sort
-+                ON
-+                    (cf_sort.icf_iss_id = iss_id AND cf_sort.icf_fld_id = $fld_id) \n";
-+        }
-+        if (!empty($options["users"]) || $options["sort_by"] === "isu_usr_id") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id";
-+        }
-+        if ((!empty($options["show_authorized_issues"])) || (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id)))) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier
-+                 ON
-+                    iur_iss_id=iss_id";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
-+                 ON
-+                    sub_iss_id=iss_id";
-+        }
-+        $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . ".`" . APP_TABLE_PREFIX . "group`
-+                 ON
-+                    iss_grp_id=grp_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id=prc_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id = pre_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 ON
-+                    iss_id=iqu_iss_id AND
-+                    (iqu_expiration > '" . Date_Helper::getCurrentDateGMT() . "' OR iqu_expiration IS NULL)
-+                 WHERE
-+                    iss_prj_id= " . Misc::escapeInteger($prj_id);
-+        $stmt .= self::buildWhereClause($options);
-+
-+        if (strstr($options["sort_by"], 'custom_field') !== false) {
-+            $fld_details = Custom_Field::getDetails($fld_id);
-+            $sort_by = 'cf_sort.' . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']);
-+        } else {
-+            $sort_by = Misc::escapeString($options["sort_by"]);
-+        }
-+
-+        $stmt .= "
-+                 GROUP BY
-+                    iss_id
-+                 ORDER BY
-+                    " . $sort_by . " " . Misc::escapeString($options["sort_order"]) . ",
-+                    iss_id DESC";
-+        $total_rows = Pager::getTotalRows($stmt);
-+        $stmt .= "
-+                 LIMIT
-+                    " . Misc::escapeInteger($start) . ", " . Misc::escapeInteger($max);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array(
-+                "list" => "",
-+                "info" => ""
-+            );
-+        } else {
-+            if (count($res) > 0) {
-+                self::getAssignedUsersByIssues($res);
-+                Time_Tracking::getTimeSpentByIssues($res);
-+                // need to get the customer titles for all of these issues...
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    Customer::getCustomerTitlesByIssues($prj_id, $res);
-+                    Customer::getSupportLevelsByIssues($prj_id, $res);
-+                }
-+                self::formatLastActionDates($res);
-+                self::getLastStatusChangeDates($prj_id, $res);
-+            } elseif ($current_row > 0) {
-+                // if there are no results, and the page is not the first page reset page to one and reload results
-+                Auth::redirect("list.php?pagerRow=0&rows=$max");
-+            }
-+            $groups = Group::getAssocList($prj_id);
-+            $categories = Category::getAssocList($prj_id);
-+            $column_headings = self::getColumnHeadings($prj_id);
-+            if (count($custom_fields) > 0) {
-+                $column_headings = array_merge($column_headings,$custom_fields);
-+            }
-+            $csv[] = @implode("\t", $column_headings);
-+            for ($i = 0; $i < count($res); $i++) {
-+                $res[$i]["time_spent"] = Misc::getFormattedTime($res[$i]["time_spent"]);
-+                $res[$i]["iss_created_date"] = Date_Helper::getFormattedDate($res[$i]["iss_created_date"]);
-+                $res[$i]["iss_expected_resolution_date"] = Date_Helper::getSimpleDate($res[$i]["iss_expected_resolution_date"], false);
-+                $fields = array(
-+                    $res[$i]['pri_title'],
-+                    $res[$i]['iss_id'],
-+                    $res[$i]['usr_full_name'],
-+                );
-+                // hide the group column from the output if no
-+                // groups are available in the database
-+                if (count($groups) > 0) {
-+                    $fields[] = $res[$i]['group'];
-+                }
-+                $fields[] = $res[$i]['assigned_users'];
-+                $fields[] = $res[$i]['time_spent'];
-+                // hide the category column from the output if no
-+                // categories are available in the database
-+                if (count($categories) > 0) {
-+                    $fields[] = $res[$i]['prc_title'];
-+                }
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    $fields[] = @$res[$i]['customer_title'];
-+                    // check if current user is acustomer and has a per incident contract.
-+                    // if so, check if issue is redeemed.
-+                    if (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer')) {
-+                        if ((Customer::hasPerIncidentContract($prj_id, self::getCustomerID($res[$i]['iss_id'])) &&
-+                                (Customer::isRedeemedIncident($prj_id, $res[$i]['iss_id'])))) {
-+                            $res[$i]['redeemed'] = true;
-+                        }
-+                    }
-+                }
-+                $fields[] = $res[$i]['sta_title'];
-+                $fields[] = $res[$i]["status_change_date"];
-+                $fields[] = $res[$i]["last_action_date"];
-+                $fields[] = $res[$i]['iss_dev_time'];
-+                $fields[] = $res[$i]['iss_summary'];
-+                $fields[] = $res[$i]['iss_expected_resolution_date'];
-+
-+                if (count($custom_fields) > 0) {
-+                    $res[$i]['custom_field'] = array();
-+                    $custom_field_values = Custom_Field::getListByIssue($prj_id, $res[$i]['iss_id']);
-+                    foreach ($custom_field_values as $this_field) {
-+                        if (!empty($custom_fields[$this_field['fld_id']])) {
-+                            $res[$i]['custom_field'][$this_field['fld_id']] = $this_field['value'];
-+                            $fields[] = $this_field['value'];
-+                        }
-+                    }
-+                }
-+
-+                $csv[] = @implode("\t", $fields);
-+            }
-+            $total_pages = ceil($total_rows / $max);
-+            $last_page = $total_pages - 1;
-+            return array(
-+                "list" => $res,
-+                "info" => array(
-+                    "current_page"  => $current_row,
-+                    "start_offset"  => $start,
-+                    "end_offset"    => $start + count($res),
-+                    "total_rows"    => $total_rows,
-+                    "total_pages"   => $total_pages,
-+                    "previous_page" => ($current_row == 0) ? "-1" : ($current_row - 1),
-+                    "next_page"     => ($current_row == $last_page) ? "-1" : ($current_row + 1),
-+                    "last_page"     => $last_page,
-+                    "custom_fields" => $custom_fields
-+                ),
-+                "csv" => @implode("\n", $csv)
-+            );
-+        }
-+    }
-+
-+
-+    /**
-+     * Processes a result set to format the "Last Action Date" column.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     */
-+    function formatLastActionDates(&$result)
-+    {
-+        for ($i = 0; $i < count($result); $i++) {
-+            if (($result[$i]['action_type'] == "internal") &&
-+                    (Auth::getCurrentRole() > User::getRoleID('Customer'))) {
-+                $label = $result[$i]["iss_last_internal_action_type"];
-+                $last_date = $result[$i]["iss_last_internal_action_date"];
-+            } else {
-+                $label = $result[$i]["iss_last_public_action_type"];
-+                $last_date = $result[$i]["iss_last_public_action_date"];
-+            }
-+            $date = new Date($last_date);
-+            $current = new Date(Date_Helper::getCurrentDateGMT());
-+            $result[$i]['last_action_date'] = sprintf("%s: %s ago", ucwords($label),
-+                    Date_Helper::getFormattedDateDiff($current->getDate(DATE_FORMAT_UNIXTIME), $date->getDate(DATE_FORMAT_UNIXTIME)));
-+        }
-+    }
-+
-+
-+    /**
-+     * Retrieves the last status change date for the given issue.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @param   array $result The associative array of data
-+     * @see     self::getListing()
-+     */
-+    function getLastStatusChangeDates($prj_id, &$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_sta_id"];
-+        }
-+        if (count($ids) == 0) {
-+            return false;
-+        }
-+        $customizations = Status::getProjectStatusCustomization($prj_id, $ids);
-+        for ($i = 0; $i < count($result); $i++) {
-+            if (empty($result[$i]['iss_sta_id'])) {
-+                $result[$i]['status_change_date'] = '';
-+            } else {
-+                list($label, $date_field_name) = @$customizations[$result[$i]['iss_sta_id']];
-+                if ((empty($label)) || (empty($date_field_name))) {
-+                    $result[$i]['status_change_date'] = '';
-+                    continue;
-+                }
-+                $current = new Date(Date_Helper::getCurrentDateGMT());
-+                $desc = "$label: %s ago";
-+                $target_date = $result[$i][$date_field_name];
-+                if (empty($target_date)) {
-+                    $result[$i]['status_change_date'] = '';
-+                    continue;
-+                }
-+                $date = new Date($target_date);
-+                $result[$i]['status_change_date'] = sprintf($desc, Date_Helper::getFormattedDateDiff($current->getDate(DATE_FORMAT_UNIXTIME), $date->getDate(DATE_FORMAT_UNIXTIME)));
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues to be displayed in the grid layout.
-+     *
-+     * @access  public
-+     * @param   array $options The search parameters
-+     * @return  string The where clause
-+     */
-+    function buildWhereClause($options)
-+    {
-+        $usr_id = Auth::getUserID();
-+        $prj_id = Auth::getCurrentProject();
-+        $role_id = User::getRoleByUser($usr_id, $prj_id);
-+
-+        $stmt = ' AND iss_usr_id = usr_id';
-+        if ($role_id == User::getRoleID('Customer')) {
-+            $stmt .= " AND iss_customer_id=" . User::getCustomerID($usr_id);
-+        } elseif (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id))) {
-+            $stmt .= " AND (
-+                        iss_usr_id = $usr_id OR
-+                        iur_usr_id = $usr_id
-+                        )";
-+        }
-+
-+        if (!empty($options["users"])) {
-+            $stmt .= " AND (\n";
-+            if (stristr($options["users"], "grp") !== false) {
-+                $chunks = explode(":", $options["users"]);
-+                $stmt .= 'iss_grp_id = ' . Misc::escapeInteger($chunks[1]);
-+            } else {
-+                if ($options['users'] == '-1') {
-+                    $stmt .= 'isu_usr_id IS NULL';
-+                } elseif ($options['users'] == '-2') {
-+                    $stmt .= 'isu_usr_id IS NULL OR isu_usr_id=' . $usr_id;
-+                } elseif ($options['users'] == '-3') {
-+                    $stmt .= 'isu_usr_id = ' . $usr_id . ' OR iss_grp_id = ' . User::getGroupID($usr_id);
-+                } elseif ($options['users'] == '-4') {
-+                    $stmt .= 'isu_usr_id IS NULL OR isu_usr_id = ' . $usr_id . ' OR iss_grp_id = ' . User::getGroupID($usr_id);
-+                } else {
-+                    $stmt .= 'isu_usr_id =' . Misc::escapeInteger($options["users"]);
-+                }
-+            }
-+            $stmt .= ')';
-+        }
-+        if (!empty($options["reporter"])) {
-+            $stmt .= " AND iss_usr_id = " . Misc::escapeInteger($options["reporter"]);
-+        }
-+        if (!empty($options["show_authorized_issues"])) {
-+            $stmt .= " AND (iur_usr_id=$usr_id)";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= " AND (sub_usr_id=$usr_id)";
-+        }
-+        if (!empty($options["keywords"])) {
-+            $stmt .= " AND (\n";
-+            if (($options['search_type'] == 'all_text') && (APP_ENABLE_FULLTEXT)) {
-+                $stmt .= "iss_id IN(" . join(', ', self::getFullTextIssues($options)) . ")";
-+            } elseif (($options['search_type'] == 'customer') && (Customer::hasCustomerIntegration($prj_id))) {
-+                // check if the user is trying to search by customer email
-+                $customer_ids = Customer::getCustomerIDsLikeEmail($prj_id, $options['keywords']);
-+                if (count($customer_ids) > 0) {
-+                    $stmt .= " iss_customer_id IN (" . implode(', ', $customer_ids) . ")";
-+                } else {
-+                    // no results, kill query
-+                    $stmt .= " iss_customer_id = -1";
-+                }
-+            } else {
-+                $stmt .= "(" . Misc::prepareBooleanSearch('iss_summary', $options["keywords"]);
-+                $stmt .= " OR " . Misc::prepareBooleanSearch('iss_description', $options["keywords"]) . ")";
-+            }
-+            $stmt .= "\n) ";
-+        }
-+        if (!empty($options["priority"])) {
-+            $stmt .= " AND iss_pri_id=" . Misc::escapeInteger($options["priority"]);
-+        }
-+        if (!empty($options["status"])) {
-+            $stmt .= " AND iss_sta_id=" . Misc::escapeInteger($options["status"]);
-+        }
-+        if (!empty($options["category"])) {
-+            if (!is_array($options['category'])) {
-+                $options['category'] = array($options['category']);
-+            }
-+            $stmt .= " AND iss_prc_id IN(" . join(', ', Misc::escapeInteger($options["category"])) . ")";
-+        }
-+        if (!empty($options["hide_closed"])) {
-+            $stmt .= " AND sta_is_closed=0";
-+        }
-+        if (!empty($options['release'])) {
-+            $stmt .= " AND iss_pre_id = " . Misc::escapeInteger($options['release']);
-+        }
-+        // now for the date fields
-+        $date_fields = array(
-+            'created_date',
-+            'updated_date',
-+            'last_response_date',
-+            'first_response_date',
-+            'closed_date'
-+        );
-+        foreach ($date_fields as $field_name) {
-+            if (!empty($options[$field_name])) {
-+                switch ($options[$field_name]['filter_type']) {
-+                    case 'greater':
-+                        $stmt .= " AND iss_$field_name >= '" . Misc::escapeString($options[$field_name]['start']) . "'";
-+                        break;
-+                    case 'less':
-+                        $stmt .= " AND iss_$field_name <= '" . Misc::escapeString($options[$field_name]['start']) . "'";
-+                        break;
-+                    case 'between':
-+                        $stmt .= " AND iss_$field_name BETWEEN '" . Misc::escapeString($options[$field_name]['start']) . "' AND '" . Misc::escapeString($options[$field_name]['end']) . "'";
-+                        break;
-+                    case 'null':
-+                        $stmt .= " AND iss_$field_name IS NULL";
-+                        break;
-+                    case 'in_past':
-+                        if (strlen($options[$field_name]['time_period']) == 0) {
-+                            $options[$field_name]['time_period'] = 0;
-+                        }
-+                        $stmt .= " AND (UNIX_TIMESTAMP('" . Date_Helper::getCurrentDateGMT() . "') - UNIX_TIMESTAMP(iss_$field_name)) <= (" .
-+                            Misc::escapeInteger($options[$field_name]['time_period']) . "*3600)";
-+                        break;
-+                }
-+            }
-+        }
-+        // custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                $fld_db_name = Custom_Field::getDBValueFieldNameByType($field['fld_type']);
-+                if (($field['fld_type'] == 'date') &&
-+                        ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . ".icf_iss_id = iss_id";
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . ".icf_fld_id = $fld_id";
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . "." . $fld_db_name . " = $cfo_id";
-+                    }
-+                } elseif ($field['fld_type'] == 'date') {
-+                    if ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day']))) {
-+                        continue;
-+                    }
-+                    $search_value = $search_value['Year'] . "-" . $search_value['Month'] . "-" . $search_value['Day'];
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id AND
-+                        cf" . $fld_id . "." . $fld_db_name . " = '" . Misc::escapeString($search_value) . "')";
-+                } else if ($field['fld_type'] == 'integer') {
-+                    $value = $search_value['value'];
-+                    switch ($search_value['filter_type']) {
-+                        case 'ge': $cmp = '>='; break;
-+                        case 'le': $cmp = '<='; break;
-+                        case 'gt': $cmp = '>'; break;
-+                        case 'lt': $cmp = '<'; break;
-+                        default:   $cmp = '='; break;
-+                    }
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id";
-+                    $stmt .= " AND\n cf" . $fld_id . ".icf_fld_id = $fld_id";
-+                    $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . $cmp . Misc::escapeString($value) . ')';
-+                } else {
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id";
-+                    $stmt .= " AND\n cf" . $fld_id . ".icf_fld_id = $fld_id";
-+                    if ($field['fld_type'] == 'combo') {
-+                        $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . " IN(" . join(', ', Misc::escapeInteger($search_value)) . ")";
-+                    } else {
-+                        $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . " LIKE '%" . Misc::escapeString($search_value) . "%'";
-+                    }
-+                    $stmt .= ')';
-+                }
-+            }
-+        }
-+        // clear cached full-text values if we are not searching fulltext anymore
-+        if ((APP_ENABLE_FULLTEXT) && (@$options['search_type'] != 'all_text')) {
-+            Session::set('fulltext_string', '');
-+            Session::set('fulltext_issues', '');
-+        }
-+        return $stmt;
-+    }
-+
-+
-+    /**
-+     * Method used to get the previous and next issues that are available
-+     * according to the current search parameters.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   array $options The search parameters
-+     * @return  array The list of issues
-+     */
-+    function getSides($issue_id, $options)
-+    {
-+        $usr_id = Auth::getUserID();
-+        $role_id = Auth::getCurrentRole();
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    " . self::getLastActionFields() . "
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user";
-+        // join custom fields if we are searching by custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                if (($field['fld_type'] == 'date') &&
-+                        ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . '_' . $cfo_id . "\n";
-+                    }
-+                } else {
-+                    $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . "\n";
-+                }
-+            }
-+        }
-+        $stmt .= ")";
-+        // check for the custom fields we want to sort by
-+        if (strstr($options['sort_by'], 'custom_field') !== false) {
-+            $fld_id = str_replace("custom_field_", '', $options['sort_by']);
-+            $stmt .= "\n LEFT JOIN \n" .
-+                APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf_sort
-+                ON
-+                    (icf_iss_id = iss_id AND icf_fld_id = $fld_id) \n";
-+        }
-+        if (!empty($options["users"]) || @$options["sort_by"] == "isu_usr_id") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id";
-+        }
-+        if ((!empty($options["show_authorized_issues"])) || (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters(Auth::getCurrentProject())))) {
-+             $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier
-+                 ON
-+                    iur_iss_id=iss_id";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
-+                 ON
-+                    sub_iss_id=iss_id";
-+        }
-+        if (@$options["sort_by"] == "pre_scheduled_date") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id = pre_id";
-+        }
-+        if (@$options['sort_by'] == 'prc_title') {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id = prc_id";
-+        }
-+        $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        $stmt .= self::buildWhereClause($options);
-+        if (strstr($options["sort_by"], 'custom_field') !== false) {
-+            $fld_details = Custom_Field::getDetails($fld_id);
-+            $sort_by = 'cf_sort.' . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']);
-+        } else {
-+            $sort_by = Misc::escapeString($options["sort_by"]);
-+        }
-+        $stmt .= "
-+                 GROUP BY
-+                    iss_id
-+                 ORDER BY
-+                    " . $sort_by . " " . Misc::escapeString($options["sort_order"]) . ",
-+                    iss_id DESC";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            // COMPAT: the next line requires PHP >= 4.0.5
-+            $index = array_search($issue_id, $res);
-+            if (!empty($res[$index+1])) {
-+                $next = $res[$index+1];
-+            }
-+            if (!empty($res[$index-1])) {
-+                $previous = $res[$index-1];
-+            }
-+            return array(
-+                "next"     => @$next,
-+                "previous" => @$previous
-+            );
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of user IDs assigned to a specific
-+     * issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of user IDs
-+     */
-+    function getAssignedUserIDs($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to see if a user is assigned to an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id An integer containg the ID of the user.
-+     * @return  boolean true if the user(s) are assigned to the issue.
-+     */
-+    function isAssignedToUser($issue_id, $usr_id)
-+    {
-+        $assigned_users = self::getAssignedUserIDs($issue_id);
-+        if (in_array($usr_id, $assigned_users)) {
-+            return true;
-+        } else {
-+            return false;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of reporters associated with a given
-+     * list of issues.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getReportersByIssues(&$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        $ids = implode(", ", $ids);
-+        $stmt = "SELECT
-+                    iss_id,
-+                    CONCAT(usr_full_name, ' <', usr_email, '>') AS usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    iss_usr_id=usr_id AND
-+                    iss_id IN ($ids)";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            // now populate the $result variable again
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['reporter'] = $res[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of assigned users by a list
-+     * of issues. This was originally created to optimize the issue
-+     * listing page.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getAssignedUsersByIssues(&$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        if (count($ids) < 1) {
-+            return;
-+        }
-+        $ids = implode(", ", $ids);
-+        $stmt = "SELECT
-+                    isu_iss_id,
-+                    isu_order,
-+                    isu_usr_id,
-+                    usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_usr_id=usr_id AND
-+                    isu_iss_id IN ($ids)";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            // gather names of the users assigned to each issue
-+            $t = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                if (!empty($t[$res[$i]['isu_iss_id']])) {
-+                    $t[$res[$i]['isu_iss_id']] .= ', ' . $res[$i]['usr_full_name'];
-+                } else {
-+                    $t[$res[$i]['isu_iss_id']] = $res[$i]['usr_full_name'];
-+                }
-+            }
-+            // gather orders
-+            $o = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                if (empty($o[$res[$i]['isu_iss_id']])) {
-+                    $o[$res[$i]['isu_iss_id']] = array();
-+                }
-+                $o[$res[$i]['isu_iss_id']][$res[$i]['isu_usr_id']] = $res[$i]['isu_order'];
-+            }
-+            // now populate the $result variable again
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['assigned_users'] = $t[$result[$i]['iss_id']];
-+                @$result[$i]['assigned_users_order'] = $o[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to add the issue description to a list of issues.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getDescriptionByIssues(&$result)
-+    {
-+        if (count($result) == 0) {
-+            return;
-+        }
-+
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        $ids = implode(", ", $ids);
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_description
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id in ($ids)";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['iss_description'] = $res[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of users (the full names) assigned to a
-+     * specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of users
-+     */
-+    function getAssignedUsers($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the details for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $force_refresh If the cache should not be used.
-+     * @return  array The details for the specified issue
-+     */
-+    function getDetails($issue_id, $force_refresh = false)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (empty($issue_id)) {
-+            return '';
-+        }
-+
-+        if ((!empty($returns[$issue_id])) && ($force_refresh != true)) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue.*,
-+                    prj_title,
-+                    prc_title,
-+                    pre_title,
-+                    pri_title,
-+                    sta_title,
-+                    sta_abbreviation,
-+                    sta_color status_color,
-+                    sta_is_closed
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project
-+                    )
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id=prc_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id=pre_id
-+                 WHERE
-+                    iss_id=$issue_id AND
-+                    iss_prj_id=prj_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            if (empty($res)) {
-+                return "";
-+            } else {
-+                $created_date_ts = Date_Helper::getUnixTimestamp($res['iss_created_date'], Date_Helper::getDefaultTimezone());
-+                // get customer information, if any
-+                if ((!empty($res['iss_customer_id'])) && (Customer::hasCustomerIntegration($res['iss_prj_id']))) {
-+                    $res['customer_business_hours'] = Customer::getBusinessHours($res['iss_prj_id'], $res['iss_customer_id']);
-+                    $res['contact_local_time'] = Date_Helper::getFormattedDate(Date_Helper::getCurrentDateGMT(), $res['iss_contact_timezone']);
-+                    $res['customer_info'] = Customer::getDetails($res['iss_prj_id'], $res['iss_customer_id'], false, $res['iss_customer_contract_id']);
-+                    $res['redeemed_incidents'] = Customer::getRedeemedIncidentDetails($res['iss_prj_id'], $res['iss_id']);
-+                    $max_first_response_time = Customer::getMaximumFirstResponseTime($res['iss_prj_id'], $res['iss_customer_id'], $res['iss_customer_contract_id']);
-+                    $res['max_first_response_time'] = Misc::getFormattedTime($max_first_response_time / 60);
-+                    if (empty($res['iss_first_response_date'])) {
-+                        $first_response_deadline = $created_date_ts + $max_first_response_time;
-+                        if (Date_Helper::getCurrentUnixTimestampGMT() <= $first_response_deadline) {
-+                            $res['max_first_response_time_left'] = Date_Helper::getFormattedDateDiff($first_response_deadline, Date_Helper::getCurrentUnixTimestampGMT());
-+                        } else {
-+                            $res['overdue_first_response_time'] = Date_Helper::getFormattedDateDiff(Date_Helper::getCurrentUnixTimestampGMT(), $first_response_deadline);
-+                        }
-+                    }
-+                }
-+                $res['iss_original_description'] = $res["iss_description"];
-+                if (!strstr($_SERVER["PHP_SELF"], 'update.php')) {
-+                    $res["iss_description"] = nl2br(htmlspecialchars($res["iss_description"]));
-+                    $res["iss_resolution"] = Resolution::getTitle($res["iss_res_id"]);
-+                }
-+                $res["iss_impact_analysis"] = nl2br(htmlspecialchars($res["iss_impact_analysis"]));
-+                $res["iss_created_date"] = Date_Helper::getFormattedDate($res["iss_created_date"]);
-+                $res['iss_created_date_ts'] = $created_date_ts;
-+                $res["assignments"] = @implode(", ", array_values(self::getAssignedUsers($res["iss_id"])));
-+                list($res['authorized_names'], $res['authorized_repliers']) = Authorized_Replier::getAuthorizedRepliers($res["iss_id"]);
-+                $temp = self::getAssignedUsersStatus($res["iss_id"]);
-+                $res["has_inactive_users"] = 0;
-+                $res["assigned_users"] = array();
-+                $res["assigned_inactive_users"] = array();
-+                foreach ($temp as $usr_id => $usr_status) {
-+                    if (!User::isActiveStatus($usr_status)) {
-+                        $res["assigned_inactive_users"][] = $usr_id;
-+                        $res["has_inactive_users"] = 1;
-+                    } else {
-+                        $res["assigned_users"][] = $usr_id;
-+                    }
-+                }
-+                if (@in_array(Auth::getUserID(), $res["assigned_users"])) {
-+                    $res["is_current_user_assigned"] = 1;
-+                } else {
-+                    $res["is_current_user_assigned"] = 0;
-+                }
-+                $res["associated_issues_details"] = self::getAssociatedIssuesDetails($res["iss_id"]);
-+                $res["associated_issues"] = self::getAssociatedIssues($res["iss_id"]);
-+                $res["reporter"] = User::getFullName($res["iss_usr_id"]);
-+                if (empty($res["iss_updated_date"])) {
-+                    $res["iss_updated_date"] = 'not updated yet';
-+                } else {
-+                    $res["iss_updated_date"] = Date_Helper::getFormattedDate($res["iss_updated_date"]);
-+                }
-+                $res["estimated_formatted_time"] = Misc::getFormattedTime($res["iss_dev_time"]);
-+                if (Release::isAssignable($res["iss_pre_id"])) {
-+                    $release = Release::getDetails($res["iss_pre_id"]);
-+                    $res["pre_title"] = $release["pre_title"];
-+                    $res["pre_status"] = $release["pre_status"];
-+                }
-+                // need to return the list of issues that are duplicates of this one
-+                $res["duplicates"] = self::getDuplicateList($res["iss_id"]);
-+                $res["duplicates_details"] = self::getDuplicateDetailsList($res["iss_id"]);
-+                // also get the issue title of the duplicated issue
-+                if (!empty($res['iss_duplicated_iss_id'])) {
-+                    $res['duplicated_issue'] = self::getDuplicatedDetails($res['iss_duplicated_iss_id']);
-+                }
-+
-+                // get group information
-+                if (!empty($res["iss_grp_id"])) {
-+                    $res["group"] = Group::getDetails($res["iss_grp_id"]);
-+                }
-+
-+                // get quarantine issue
-+                $res["quarantine"] = self::getQuarantineInfo($res["iss_id"]);
-+
-+                $returns[$issue_id] = $res;
-+                return $res;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get some simple details about the given duplicated issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The duplicated issue details
-+     */
-+    function getDuplicatedDetails($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_summary title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_sta_id=sta_id AND
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to bulk update a list of issues
-+     *
-+     * @access  public
-+     * @return  boolean
-+     */
-+    function bulkUpdate()
-+    {
-+        // check if user performing this chance has the proper role
-+        if (Auth::getCurrentRole() < User::getRoleID('Manager')) {
-+            return -1;
-+        }
-+
-+        $items = Misc::escapeInteger($_POST['item']);
-+        $new_status_id = Misc::escapeInteger($_POST['status']);
-+        $new_release_id = Misc::escapeInteger(@$_POST['release']);
-+        $new_priority_id = Misc::escapeInteger($_POST['priority']);
-+        $new_category_id = Misc::escapeInteger($_POST['category']);
-+
-+        for ($i = 0; $i < count($items); $i++) {
-+            if (!self::canAccess($items[$i], Auth::getUserID())) {
-+                continue;
-+            } elseif (self::getProjectID($_POST['item'][$i]) != Auth::getCurrentProject()) {
-+                // make sure issue is not in another project
-+                continue;
-+            }
-+
-+            $updated_fields = array();
-+
-+            // update assignment
-+            if (count(@$_POST['users']) > 0) {
-+                $users = Misc::escapeInteger($_POST['users']);
-+                // get who this issue is currently assigned too
-+                $stmt = "SELECT
-+                            isu_usr_id,
-+                            usr_full_name
-+                         FROM
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                         WHERE
-+                            isu_usr_id = usr_id AND
-+                            isu_iss_id = " . $items[$i];
-+                $current_assignees = DB_Helper::getInstance()->getAssoc($stmt);
-+                if (PEAR::isError($current_assignees)) {
-+                    Error_Handler::logError(array($current_assignees->getMessage(), $current_assignees->getDebugInfo()), __FILE__, __LINE__);
-+                    return -1;
-+                }
-+                foreach ($current_assignees as $usr_id => $usr_name) {
-+                    if (!in_array($usr_id, $users)) {
-+                        self::deleteUserAssociation($items[$i], $usr_id, false);
-+                    }
-+                }
-+                $new_user_names = array();
-+                $new_assignees = array();
-+                foreach ($users as $usr_id) {
-+                    $new_user_names[$usr_id] = User::getFullName($usr_id);
-+
-+                    // check if the issue is already assigned to this person
-+                    $stmt = "SELECT
-+                                COUNT(*) AS total
-+                             FROM
-+                                " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                             WHERE
-+                                isu_iss_id=" . $items[$i] . " AND
-+                                isu_usr_id=" . $usr_id;
-+                    $total = DB_Helper::getInstance()->getOne($stmt);
-+                    if ($total > 0) {
-+                        continue;
-+                    } else {
-+                        $new_assignees[] = $usr_id;
-+                        // add the assignment
-+                        self::addUserAssociation(Auth::getUserID(), $items[$i], $usr_id, false);
-+                        Notification::subscribeUser(Auth::getUserID(), $items[$i], $usr_id, Notification::getAllActions());
-+                        Workflow::handleAssignment(Auth::getCurrentProject(), $items[$i], Auth::getUserID());
-+                    }
-+                }
-+                Notification::notifyNewAssignment($new_assignees, $items[$i]);
-+                $updated_fields['Assignment'] = History::formatChanges(join(', ', $current_assignees), join(', ', $new_user_names));
-+            }
-+
-+            // update status
-+            if (!empty($new_status_id)) {
-+                $old_status_id = self::getStatusID($items[$i]);
-+                $res = self::setStatus($items[$i], $new_status_id, false);
-+                if ($res == 1) {
-+                    $updated_fields['Status'] = History::formatChanges(Status::getStatusTitle($old_status_id), Status::getStatusTitle($new_status_id));
-+                }
-+            }
-+
-+            // update release
-+            if (!empty($new_release_id)) {
-+                $old_release_id = self::getRelease($items[$i]);
-+                $res = self::setRelease($items[$i], $new_release_id);
-+                if ($res == 1) {
-+                    $updated_fields['Release'] = History::formatChanges(Release::getTitle($old_release_id), Release::getTitle($new_release_id));
-+                }
-+            }
-+
-+            // update priority
-+            if (!empty($new_priority_id)) {
-+                $old_priority_id = self::getPriority($items[$i]);
-+                $res = self::setPriority($items[$i], $new_priority_id);
-+                if ($res == 1) {
-+                    $updated_fields['Priority'] = History::formatChanges(Priority::getTitle($old_priority_id), Priority::getTitle($new_priority_id));
-+                }
-+            }
-+
-+            // update category
-+            if (!empty($new_category_id)) {
-+                $old_category_id = self::getCategory($items[$i]);
-+                $res = self::setCategory($items[$i], $new_category_id);
-+                if ($res == 1) {
-+                    $updated_fields['Category'] = History::formatChanges(Category::getTitle($old_category_id), Category::getTitle($new_category_id));
-+                }
-+            }
-+
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $k = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($k > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    $changes .= "$key: $value";
-+                    $k++;
-+                }
-+                History::add($items[$i], Auth::getUserID(), History::getTypeID('issue_bulk_updated'), "Issue updated ($changes) by " . User::getFullName(Auth::getUserID()));
-+            }
-+
-+            // close if request
-+            if ((isset($_REQUEST['closed_status'])) && (!empty($_REQUEST['closed_status']))) {
-+                self::close(Auth::getUserID(), $items[$i], true, 0, Misc::escapeInteger($_REQUEST['closed_status']), Misc::escapeString($_REQUEST['closed_message']), $_REQUEST['notification_list']);
-+            }
-+        }
-+        return true;
-+    }
-+
-+
-+    /**
-+     * Method used to set the initial impact analysis for a specific issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setImpactAnalysis($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='update',
-+                    iss_developer_est_time=" . Misc::escapeInteger($_POST["dev_time"]) . ",
-+                    iss_impact_analysis='" . Misc::escapeString($_POST["impact_analysis"]) . "'
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // add the impact analysis to the history of the issue
-+            $summary = 'Initial Impact Analysis for issue set by ' . User::getFullName(Auth::getUserID());
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('impact_analysis_added'), $summary);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs that area available in the
-+     * system.
-+     *
-+     * @access  public
-+     * @param   string $extra_condition An extra condition in the WHERE clause
-+     * @return  array The list of issue IDs
-+     */
-+    function getColList($extra_condition = NULL)
-+    {
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        if (!empty($extra_condition)) {
-+            $stmt .= " AND $extra_condition ";
-+        }
-+        $stmt .= "
-+                 ORDER BY
-+                    iss_id DESC";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs and their respective
-+     * titles.
-+     *
-+     * @access  public
-+     * @param   string $extra_condition An extra condition in the WHERE clause
-+     * @return  array The list of issues
-+     */
-+    function getAssocList($extra_condition = NULL)
-+    {
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        if (!empty($extra_condition)) {
-+            $stmt .= " AND $extra_condition ";
-+        }
-+        $stmt .= "
-+                 ORDER BY
-+                    iss_id ASC";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues associated to a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of associated issues
-+     */
-+    function getAssociatedIssues($issue_id)
-+    {
-+        $issues = self::getAssociatedIssuesDetails($issue_id);
-+        $associated = array();
-+        for ($i = 0; $i < count($issues); $i++) {
-+            $associated[] = $issues[$i]['associated_issue'];
-+        }
-+        return $associated;
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues associated details to a
-+     * specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of associated issues
-+     */
-+    function getAssociatedIssuesDetails($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    isa_associated_id associated_issue,
-+                    iss_summary associated_title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    isa_associated_id=iss_id AND
-+                    iss_sta_id=sta_id AND
-+                    isa_issue_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to check whether an issue was already closed or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  boolean
-+     */
-+    function isClosed($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    iss_sta_id=sta_id AND
-+                    sta_is_closed=1";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns a simple list of issues that are currently set to some
-+     * form of quarantine. This is mainly used by the IRC interface.
-+     *
-+     * @access  public
-+     * @return  array List of quarantined issues
-+     */
-+    function getQuarantinedIssueList()
-+    {
-+        // XXX: would be nice to restrict the result list to only one project
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id=iss_id AND
-+                    iqu_expiration >= '" . Date_Helper::getCurrentDateGMT() . "' AND
-+                    iqu_expiration IS NOT NULL";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            self::getAssignedUsersByIssues($res);
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the status of a quarantine.
-+     *
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer Indicates what the current state of quarantine is.
-+     */
-+    function getQuarantineInfo($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iqu_status,
-+                    iqu_expiration
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id = " . Misc::escapeInteger($issue_id) . " AND
-+                        (iqu_expiration > '" . Date_Helper::getCurrentDateGMT() . "' OR
-+                        iqu_expiration IS NULL)";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            if (!empty($res["iqu_expiration"])) {
-+                $expiration_ts = Date_Helper::getUnixTimestamp($res['iqu_expiration'], Date_Helper::getDefaultTimezone());
-+                $res["time_till_expiration"] = Date_Helper::getFormattedDateDiff($expiration_ts, Date_Helper::getCurrentUnixTimestampGMT());
-+            }
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Sets the quarantine status. Optionally an expiration date can be set
-+     * to indicate when the quarantine expires. A status > 0 indicates that quarantine is active.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $status The quarantine status
-+     * @param   string  $expiration The expiration date of quarantine (default empty)
-+     */
-+    function setQuarantine($issue_id, $status, $expiration = '')
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $status = Misc::escapeInteger($status);
-+
-+        // see if there is an existing record
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id = $issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        if ($res > 0) {
-+            // update
-+            $stmt = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                     SET
-+                        iqu_status = $status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\niqu_expiration = '" . Misc::escapeString($expiration) . "'";
-+            }
-+            $stmt .= "\nWHERE
-+                        iqu_iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                // add history entry about this change taking place
-+                if ($status == 0) {
-+                    History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_quarantine_removed'),
-+                            "Issue quarantine status cleared by " . User::getFullName(Auth::getUserID()));
-+                }
-+            }
-+        } else {
-+            // insert
-+            $stmt = "INSERT INTO
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                     (
-+                        iqu_iss_id,
-+                        iqu_status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\niqu_expiration\n";
-+            }
-+            $stmt .= ") VALUES (
-+                        $issue_id,
-+                        $status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\n'" . Misc::escapeString($expiration) . "'\n";
-+            }
-+            $stmt .= ")";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Sets the group of the issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $group_id The ID of the group
-+     * @return  integer 1 if successful, -1 or -2 otherwise
-+     */
-+    function setGroup($issue_id, $group_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $group_id = Misc::escapeInteger($group_id);
-+
-+        $current = self::getDetails($issue_id);
-+        if ($current["iss_grp_id"] == $group_id) {
-+            return -2;
-+        }
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_grp_id = $group_id
-+                 WHERE
-+                    iss_id = $issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        $current_user = Auth::getUserID();
-+        if (empty($current_user)) {
-+            $current_user = APP_SYSTEM_USER_ID;
-+        }
-+        History::add($issue_id, $current_user, History::getTypeID('group_changed'),
-+                "Group changed (" . History::formatChanges(Group::getName($current["iss_grp_id"]), Group::getName($group_id)) . ") by " . User::getFullName($current_user));
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Returns the group ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The associated group ID
-+     */
-+    function getGroupID($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_grp_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns an array of issues based on full text search results.
-+     *
-+     * @param   array $options An array of search options
-+     * @return  array An array of issue IDS
-+     */
-+    function getFullTextIssues($options)
-+    {
-+        // check if a list of issues for this full text search is already cached
-+        $fulltext_string = Session::get('fulltext_string');
-+        if ((!empty($fulltext_string)) && ($fulltext_string == $options['keywords'])) {
-+            return Session::get('fulltext_issues');
-+        }
-+
-+        // no pre-existing list, generate them
-+        $stmt = "(SELECT
-+                    DISTINCT(iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                     MATCH(iss_summary, iss_description) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(not_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "note
-+                 WHERE
-+                     MATCH(not_note) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(ttr_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking
-+                 WHERE
-+                     MATCH(ttr_summary) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(phs_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "phone_support
-+                 WHERE
-+                     MATCH(phs_description) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                     DISTINCT(sup_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
-+                 WHERE
-+                     sup_id = seb_sup_id AND
-+                     MATCH(seb_body) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 )";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array(-1);
-+        } else {
-+            $stmt = "SELECT
-+                        DISTINCT(icf_iss_id)
-+                    FROM
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
-+                    WHERE
-+                        MATCH (icf_value) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)";
-+            $custom_res = DB_Helper::getInstance()->getCol($stmt);
-+            if (PEAR::isError($custom_res)) {
-+                Error_Handler::logError(array($custom_res->getMessage(), $custom_res->getDebugInfo()), __FILE__, __LINE__);
-+                return array(-1);
-+            }
-+            $issues = array_merge($res, $custom_res);
-+            // we kill the query results on purpose to flag that no
-+            // issues could be found with fulltext search
-+            if (count($issues) < 1) {
-+                $issues = array(-1);
-+            }
-+            Session::set('fulltext_string', $options['keywords']);
-+            Session::set('fulltext_issues', $issues);
-+            return $issues;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method to determine if user can access a particular issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue.
-+     * @param   integer $usr_id The ID of the user
-+     * @return  boolean If the user can access the issue
-+     */
-+    function canAccess($issue_id, $usr_id)
-+    {
-+        static $access;
-+
-+        if (empty($issue_id)) {
-+            return true;
-+        }
-+
-+        if (isset($access[$issue_id . "-" . $usr_id])) {
-+            return $access[$issue_id . "-" . $usr_id];
-+        }
-+
-+        $details = self::getDetails($issue_id);
-+        if (empty($details)) {
-+            return true;
-+        }
-+        $usr_details = User::getDetails($usr_id);
-+        $usr_role = User::getRoleByUser($usr_id, $details['iss_prj_id']);
-+        $prj_id = self::getProjectID($issue_id);
-+
-+
-+        if (empty($usr_role)) {
-+            // check if they are even allowed to access the project
-+            $return = false;
-+        } elseif ((Customer::hasCustomerIntegration($details['iss_prj_id'])) && ($usr_role == User::getRoleID("Customer")) &&
-+                ($details['iss_customer_id'] != $usr_details['usr_customer_id'])) {
-+            // check customer permissions
-+            $return = false;
-+        } elseif ($details['iss_private'] == 1) {
-+            // check if the issue is even private
-+
-+            // check role, reporter, assigment and group
-+            if ($usr_role > User::getRoleID("Developer")) {
-+                $return = true;
-+            } elseif ($details['iss_usr_id'] == $usr_id) {
-+                $return = true;
-+            } elseif (self::isAssignedToUser($issue_id, $usr_id)) {
-+                $return = true;
-+            } elseif ((!empty($details['iss_grp_id'])) && (!empty($usr_details['usr_grp_id'])) &&
-+                        ($details['iss_grp_id'] == $usr_details['usr_grp_id'])) {
-+                $return = true;
-+            } elseif (Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id)) {
-+                $return = true;
-+            } else {
-+                $return = false;
-+            }
-+        } elseif ((Auth::getCurrentRole() == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id)) &&
-+                ($details['iss_usr_id'] != $usr_id) && (!Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id))) {
-+            return false;
-+        } else {
-+            $return = true;
-+        }
-+
-+        $access[$issue_id . "-" . $usr_id] = $return;
-+        return $return;
-+    }
-+
-+
-+    /**
-+     * Returns true if the specified issue is private, false otherwise
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  boolean If the issue is private or not
-+     */
-+    function isPrivate($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!isset($returns[$issue_id])) {
-+            $sql = "SELECT
-+                        iss_private
-+                    FROM
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    WHERE
-+                        iss_id=$issue_id";
-+            $res = DB_Helper::getInstance()->getOne($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return true;
-+            } else {
-+                if ($res == 1) {
-+                    $returns[$issue_id] = true;
-+                } else {
-+                    $returns[$issue_id] = false;
-+                }
-+            }
-+        }
-+        return $returns[$issue_id];
-+    }
-+
-+
-+    /**
-+     * Clears closed information from an issues.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     */
-+    function clearClosed($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_closed_date = null,
-+                    iss_res_id = null
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        self::moveOrderForAllUsers($issue_id, 1);
-+    }
-+
-+
-+    /**
-+     * Returns the message ID that should be used as the parent ID for all messages
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     */
-+    function getRootMessageID($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_root_message_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the issue ID of the issue with the specified root message ID, or false
-+     * @access  public
-+     * @param   string $msg_id The Message ID
-+     * @return  integer The ID of the issue
-+     */
-+    function getIssueByRootMessageID($msg_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$msg_id])) {
-+            return $returns[$msg_id];
-+        }
-+        $sql = "SELECT
-+                    iss_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_root_message_id = '" . Misc::escapeString($msg_id) . "'";
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        }
-+        if (empty($res)) {
-+            $returns[$msg_id] = false;
-+        } else {
-+            $returns[$msg_id] =  $res;
-+        }
-+        return $returns[$msg_id];
-+    }
-+
-+
-+    /**
-+     * Sets the assignees for the issue
-+     *
-+     * @param   integer $issue_id
-+     * @param   array   $assignees
-+     */
-+    function setAssignees($issue_id, $assignees)
-+    {
-+        if (!is_array($assignees)) {
-+            $assignees = array();
-+        }
-+
-+        // see if there is anything to change
-+        $old_assignees = self::getAssignedUserIDs($issue_id);
-+        if ((count(array_diff($old_assignees, $assignees)) == 0) && (count(array_diff($assignees, $old_assignees)) == 0)) {
-+            return;
-+        }
-+
-+        $old_assignee_names = self::getAssignedUsers($issue_id);
-+
-+        Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, Auth::getUserID(), self::getDetails($issue_id), $assignees, true);
-+        // clear up the assignments for this issue, and then assign it to the current user
-+        self::deleteUserAssociations($issue_id);
-+        $assignee_names = array();
-+        foreach ($assignees as $assignee) {
-+            $res = self::addUserAssociation(Auth::getUserID(), $issue_id, $assignee, false);
-+            if ($res == -1) {
-+                return false;
-+            }
-+            $assignee_names[] = User::getFullName($assignee);
-+            Notification::subscribeUser(Auth::getUserID(), $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'set_assignees'), false);
-+        }
-+
-+        Notification::notifyNewAssignment($assignees, $issue_id);
-+
-+        // save a history entry about this...
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('user_associated'),
-+                        "Issue assignment to changed (" . History::formatChanges(join(', ', $old_assignee_names), join(', ', $assignee_names)) . ") by " . User::getFullName(Auth::getUserID()));
-+    }
-+
-+    /**
-+     * Reorders user's issues as requested by user
-+     * @access public
-+     * @param $usr_id User to be reordered
-+     * @param $issue_id Issue or array of issues to be moved
-+     * @param $neworder The new order of the issues
-+     * @return void
-+     */
-+    function reorderUserIssues($usr_id, $issue_id, $neworder)
-+    {
-+        if (!isset($usr_id) || !isset($issue_id) || !isset($neworder)) {
-+            return false;
-+        }
-+        if (!is_numeric($usr_id) || !is_numeric($neworder)) {
-+            return false;
-+        }
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $neworder = Misc::escapeInteger($neworder);
-+        if (is_array($issue_id)) {
-+            $issue_count = count($issue_id);
-+            $issue_id_str = implode(", ", $issue_id);
-+        } else {
-+            $issue_count = 1;
-+            $issue_id_str = $issue_id;
-+            $issue_id = array($issue_id);
-+        }
-+        // do a nasty pretending to be deleting stuff so that reordering happens as if these elements were deleted
-+        $orderlist = self::getDeleteUserAssociationOrderList($issue_id_str, $usr_id);
-+        self::rearrangeDeleteUserAssociationOrderList($orderlist);
-+        // move down the orders to free the "order space" needed
-+        $stmt = "UPDATE 
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 SET
-+                    isu_order = isu_order + $issue_count
-+                 WHERE
-+                    isu_usr_id = $usr_id AND
-+                    isu_order >= $neworder";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        //update the order for the issues being moved
-+        $i = 0;
-+        foreach ($issue_id as $iss_id) {
-+            $stmt = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                     SET
-+                        isu_order = " . ($neworder + $i) . "
-+                     WHERE
-+                        isu_usr_id = $usr_id AND
-+                        isu_iss_id = $iss_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+            $i++;
-+        }
-+    }
-+
-+
-+    /**
-+     * Get users issue order list
-+     * @access public
-+     * @param $user_id User
-+     * @param $order_list Order of the issues
-+     * @return void
-+     */
-+    function getIssueOrderByUser($usr_id) {
-+
-+        if (!is_numeric($usr_id)) {
-+            return false;
-+        }
-+
-+        $stmt = "SELECT
-+                    isu_iss_id, isu_order
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                WHERE
-+                    isu_usr_id = " . $usr_id ;
-+
-+        $order_list = array();
-+
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            foreach ($res as $row) {
-+                $order_list[$row["isu_iss_id"]] = $row["isu_order"];
-+            }
-+        }
-+        return $order_list;
-+    }
-+
-+    function moveOrderForAllUsers($issue_id, $neworder)
-+    {
-+        // Move the issue to the top priority for the ppl it's assigned to
-+        $stmt = "SELECT isu_usr_id FROM
-+                    "  . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        foreach ($res as $row) {
-+            self::reorderUserIssues($row["isu_usr_id"], $issue_id, $neworder);
-+        }
-+    }
-+    
-+}
---- eventum-2.2/lib/eventum/class.issue.php.~3~        1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/lib/eventum/class.issue.php.~3~  2009-10-12 22:10:36.442518536 +0300
-@@ -0,0 +1,4752 @@
-+<?php
-+/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
-+// +----------------------------------------------------------------------+
-+// | Eventum - Issue Tracking System                                      |
-+// +----------------------------------------------------------------------+
-+// | Copyright (c) 2003 - 2008 MySQL AB                                   |
-+// | Copyright (c) 2008 - 2009 Sun Microsystem Inc.                       |
-+// |                                                                      |
-+// | This program is free software; you can redistribute it and/or modify |
-+// | it under the terms of the GNU General Public License as published by |
-+// | the Free Software Foundation; either version 2 of the License, or    |
-+// | (at your option) any later version.                                  |
-+// |                                                                      |
-+// | This program is distributed in the hope that it will be useful,      |
-+// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
-+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
-+// | GNU General Public License for more details.                         |
-+// |                                                                      |
-+// | You should have received a copy of the GNU General Public License    |
-+// | along with this program; if not, write to:                           |
-+// |                                                                      |
-+// | Free Software Foundation, Inc.                                       |
-+// | 59 Temple Place - Suite 330                                          |
-+// | Boston, MA 02111-1307, USA.                                          |
-+// +----------------------------------------------------------------------+
-+// | Authors: João Prado Maia <jpm@mysql.com>                             |
-+// +----------------------------------------------------------------------+
-+//
-+
-+
-+/**
-+ * Class designed to handle all business logic related to the issues in the
-+ * system, such as adding or updating them or listing them in the grid mode.
-+ *
-+ * @author  João Prado Maia <jpm@mysql.com>
-+ * @version $Revision$
-+ */
-+
-+class Issue
-+{
-+    /**
-+     * Method used to check whether a given issue ID exists or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $check_project If we should check that this issue is in the current project
-+     * @return  boolean
-+     */
-+    function exists($issue_id, $check_project = true)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        if ($check_project) {
-+            $stmt .= " AND
-+                    iss_prj_id = " . Auth::getCurrentProject();
-+        }
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of column heading titles for the
-+     * CSV export functionality of the issue listing screen.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @return  array The list of column heading titles
-+     */
-+    function getColumnHeadings($prj_id)
-+    {
-+        $headings = array(
-+            'Priority',
-+            'Issue ID',
-+            'Reporter',
-+        );
-+        // hide the group column from the output if no
-+        // groups are available in the database
-+        $groups = Group::getAssocList($prj_id);
-+        if (count($groups) > 0) {
-+            $headings[] = 'Group';
-+        }
-+        $headings[] = 'Assigned';
-+        $headings[] = 'Time Spent';
-+        // hide the category column from the output if no
-+        // categories are available in the database
-+        $categories = Category::getAssocList($prj_id);
-+        if (count($categories) > 0) {
-+            $headings[] = 'Category';
-+        }
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $headings[] = 'Customer';
-+        }
-+        $headings[] = 'Status';
-+        $headings[] = 'Status Change Date';
-+        $headings[] = 'Last Action Date';
-+        $headings[] = 'Est. Dev. TIme';
-+        $headings[] = 'Summary';
-+        $headings[] = 'Expected Resolution Date';
-+        return $headings;
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of date fields available to issues, to
-+     * be used when customizing the issue listing screen in the 'last status
-+     * change date' column.
-+     *
-+     * @access  public
-+     * @param   boolean $display_customer_fields Whether to include any customer related fields or not
-+     * @return  array The list of available date fields
-+     */
-+    function getDateFieldsAssocList($display_customer_fields = FALSE)
-+    {
-+        $fields = array(
-+            'iss_created_date'              => 'Created Date',
-+            'iss_updated_date'              => 'Last Updated Date',
-+            'iss_last_response_date'        => 'Last Response Date',
-+            'iss_closed_date'               => 'Closed Date'
-+        );
-+        if ($display_customer_fields) {
-+            $fields['iss_last_customer_action_date'] = 'Customer Action Date';
-+        }
-+        asort($fields);
-+        return $fields;
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs and their respective
-+     * titles associated to a given project.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @return  array The list of issues
-+     */
-+    function getAssocListByProject($prj_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Misc::escapeInteger($prj_id) . "
-+                 ORDER BY
-+                    iss_id ASC";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The status ID
-+     */
-+    function getStatusID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_sta_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Records the last customer action date for a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function recordLastCustomerAction($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_last_customer_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='customer action'
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the customer ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getCustomerID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the contract ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getContractID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_contract_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Sets the contract ID for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer The contract ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setContractID($issue_id, $contract_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $old_contract_id = self::getContractID($issue_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                SET
-+                    iss_customer_contract_id = " . Misc::escapeInteger($contract_id) . "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // log this
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID("contract_changed"), "Contract changed from $old_contract_id to $contract_id by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the customer ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The customer ID associated with the issue
-+     */
-+    function getContactID($issue_id)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_customer_contact_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the project associated to a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $force_refresh If the cache should not be used.
-+     * @return  integer The project ID
-+     */
-+    function getProjectID($issue_id, $force_refresh = false)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if ((!empty($returns[$issue_id])) && ($force_refresh != true)) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_prj_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remotely assign a given issue to an user.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing the change
-+     * @param   boolean $assignee The user ID of the assignee
-+     * @return  integer The status ID
-+     */
-+    function remoteAssign($issue_id, $usr_id, $assignee)
-+    {
-+        Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, $usr_id, self::getDetails($issue_id), array($assignee), true);
-+        // clear up the assignments for this issue, and then assign it to the current user
-+        self::deleteUserAssociations($issue_id, $usr_id);
-+        $res = self::addUserAssociation($usr_id, $issue_id, $assignee, false);
-+        if ($res != -1) {
-+            // save a history entry about this...
-+            History::add($issue_id, $usr_id, History::getTypeID('remote_assigned'), "Issue remotely assigned to " . User::getFullName($assignee) . " by " . User::getFullName($usr_id));
-+            Notification::subscribeUser($usr_id, $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'remote_assign'), false);
-+            if ($assignee != $usr_id) {
-+                Notification::notifyNewAssignment(array($assignee), $issue_id);
-+            }
-+        }
-+        return $res;
-+    }
-+
-+
-+    /**
-+     * Method used to set the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $status_id The new status ID
-+     * @param   boolean $notify If a notification should be sent about this change.
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setStatus($issue_id, $status_id, $notify = false)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+
-+        $workflow = Workflow::preStatusChange(self::getProjectID($issue_id), $issue_id, $status_id, $notify);
-+        if ($workflow !== true) {
-+            return $workflow;
-+        }
-+
-+        // check if the status is already set to the 'new' one
-+        if (self::getStatusID($issue_id) == $status_id) {
-+            return -1;
-+        }
-+
-+        $old_status = self::getStatusID($issue_id);
-+        $old_details = Status::getDetails($old_status);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_sta_id=$status_id,
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='update'
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // clear out the last-triggered-reminder flag when changing the status of an issue
-+            Reminder_Action::clearLastTriggered($issue_id);
-+
-+            // if old status was closed and new status is not, clear closed data from issue.
-+            if (@$old_details['sta_is_closed'] == 1) {
-+                $new_details = Status::getDetails($status_id);
-+                if ($new_details['sta_is_closed'] != 1) {
-+                    self::clearClosed($issue_id);
-+                }
-+            }
-+
-+            if ($notify) {
-+                Notification::notifyStatusChange($issue_id, $old_status, $status_id);
-+            }
-+
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remotely set the status of a given issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing this change
-+     * @param   integer $new_status The new status ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setRemoteStatus($issue_id, $usr_id, $new_status)
-+    {
-+        $sta_id = Status::getStatusID($new_status);
-+
-+        $res = self::setStatus($issue_id, $sta_id);
-+        if ($res == 1) {
-+            // record history entry
-+            History::add($issue_id, $usr_id, History::getTypeID('remote_status_change'), "Status remotely changed to '$new_status' by " . User::getFullName($usr_id));
-+        }
-+        return $res;
-+    }
-+
-+
-+    /**
-+     * Method used to set the release of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $pre_id The ID of the release to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setRelease($issue_id, $pre_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $pre_id = Misc::escapeInteger($pre_id);
-+
-+        if ($pre_id != self::getRelease($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_pre_id = $pre_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current release of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The release
-+     */
-+    function getRelease($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_pre_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to set the priority of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $pri_id The ID of the priority to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setPriority($issue_id, $pri_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $pri_id = Misc::escapeInteger($pri_id);
-+
-+        if ($pri_id != self::getPriority($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_pri_id = $pri_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current issue priority
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The priority
-+     */
-+    function getPriority($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_pri_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to set the category of an issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $prc_id The ID of the category to set this issue too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setCategory($issue_id, $prc_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $prc_id = Misc::escapeInteger($prc_id);
-+
-+        if ($prc_id != self::getPriority($issue_id)) {
-+            $sql = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    SET
-+                        iss_prc_id = $prc_id
-+                    WHERE
-+                        iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                return 1;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the current issue category
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  integer The category
-+     */
-+    function getCategory($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_prc_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get all issues associated with a status that doesn't have
-+     * the 'closed' context.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID to list issues from
-+     * @param   integer $usr_id The user ID of the user requesting this information
-+     * @param   boolean $show_all_issues Whether to show all open issues, or just the ones assigned to the given email address
-+     * @param   integer $status_id The status ID to be used to restrict results
-+     * @return  array The list of open issues
-+     */
-+    function getOpenIssues($prj_id, $usr_id, $show_all_issues, $status_id)
-+    {
-+        $prj_id = Misc::escapeInteger($prj_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+        $projects = Project::getRemoteAssocListByUser($usr_id);
-+        if (@count($projects) == 0) {
-+            return '';
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary,
-+                    sta_title
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                    )
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id
-+                 WHERE ";
-+        if (!empty($status_id)) {
-+            $stmt .= " sta_id=$status_id AND ";
-+        }
-+        $stmt .= "
-+                    iss_prj_id=$prj_id AND
-+                    sta_id=iss_sta_id AND
-+                    sta_is_closed=0";
-+        if ($show_all_issues == false) {
-+            $stmt .= " AND
-+                    isu_usr_id=$usr_id";
-+        }
-+        $stmt .= "\nGROUP BY
-+                        iss_id";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            if (count($res) > 0) {
-+                self::getAssignedUsersByIssues($res);
-+            }
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to build the required parameters to simulate an email reply
-+     * to the user who reported the issue, using the issue details like summary
-+     * and description as email fields.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The email parameters
-+     */
-+    function getReplyDetails($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $stmt = "SELECT
-+                    iss_created_date,
-+                    usr_full_name AS reporter,
-+                    usr_email AS reporter_email,
-+                    iss_description AS description,
-+                    iss_summary AS sup_subject
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    iss_usr_id=usr_id AND
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return '';
-+        } else {
-+            $res['reply_subject'] = 'Re: [#' . $issue_id . '] ' . $res["sup_subject"];
-+            $res['created_date_ts'] = Date_Helper::getUnixTimestamp($res['iss_created_date'], 'GMT');
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to record the last updated timestamp for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   string $type The type of update that was made (optional)
-+     * @return  boolean
-+     */
-+    function markAsUpdated($issue_id, $type = false)
-+    {
-+        $public = array("staff response", "customer action", "file uploaded", "user response");
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "'\n";
-+        if ($type != false) {
-+            if (in_array($type, $public)) {
-+                $field = "iss_last_public_action_";
-+            } else {
-+                $field = "iss_last_internal_action_";
-+            }
-+            $stmt .= ",\n " . $field . "date = '" . Date_Helper::getCurrentDateGMT() . "',\n" .
-+                $field . "type  ='" . Misc::escapeString($type) . "'\n";
-+        }
-+        $stmt .= "WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            // update last response dates if this is a staff response
-+            if ($type == "staff response") {
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         SET
-+                            iss_last_response_date='" . Date_Helper::getCurrentDateGMT() . "'
-+                         WHERE
-+                            iss_id = " . Misc::escapeInteger($issue_id);
-+                DB_Helper::getInstance()->query($stmt);
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         SET
-+                            iss_first_response_date='" . Date_Helper::getCurrentDateGMT() . "'
-+                         WHERE
-+                            iss_first_response_date IS NULL AND
-+                            iss_id = " . Misc::escapeInteger($issue_id);
-+                DB_Helper::getInstance()->query($stmt);
-+            }
-+
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to check whether a given issue has duplicates
-+     * or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  boolean
-+     */
-+    function hasDuplicates($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(iss_id)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to update the duplicated issues for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function updateDuplicates($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $ids = self::getDuplicateList($issue_id);
-+        if ($ids == '') {
-+            return -1;
-+        }
-+        $ids = @array_keys($ids);
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_prc_id=" . Misc::escapeInteger($_POST["category"]) . ",";
-+        if (@$_POST["keep"] == "no") {
-+            $stmt .= "iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",";
-+        }
-+        $stmt .= "
-+                    iss_pri_id=" . Misc::escapeInteger($_POST["priority"]) . ",
-+                    iss_sta_id=" . Misc::escapeInteger($_POST["status"]) . ",
-+                    iss_res_id=" . Misc::escapeInteger($_POST["resolution"]) . "
-+                 WHERE
-+                    iss_id IN (" . implode(", ", $ids) . ")";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // record the change
-+            for ($i = 0; $i < count($ids); $i++) {
-+                History::add($ids[$i], Auth::getUserID(), History::getTypeID('duplicate_update'),
-+                    "The details for issue #$issue_id were updated by " . User::getFullName(Auth::getUserID()) . " and the changes propagated to the duplicated issues.");
-+            }
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get a list of the duplicate issues for a given
-+     * issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of duplicates
-+     */
-+    function getDuplicateList($issue_id)
-+    {
-+        $res = self::getDuplicateDetailsList($issue_id);
-+        if (@count($res) == 0) {
-+            return '';
-+        } else {
-+            $list = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                $list[$res[$i]['issue_id']] = $res[$i]['title'];
-+            }
-+            return $list;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get a list of the duplicate issues (and their details)
-+     * for a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of duplicates
-+     */
-+    function getDuplicateDetailsList($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    iss_id issue_id,
-+                    iss_summary title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_sta_id=sta_id AND
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to clear the duplicate status of an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function clearDuplicateStatus($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_duplicated_iss_id=NULL
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // record the change
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('duplicate_removed'), "Duplicate flag was reset by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to mark an issue as a duplicate of an existing one.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function markAsDuplicate($issue_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (!self::exists($issue_id)) {
-+            return -1;
-+        }
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='updated',
-+                    iss_duplicated_iss_id=" . Misc::escapeInteger($_POST["duplicated_issue"]) . "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if (!empty($_POST["comments"])) {
-+                // add note with the comments of marking an issue as a duplicate of another one
-+                $_POST['title'] = 'Issue duplication comments';
-+                $_POST["note"] = $_POST["comments"];
-+                Note::insert(Auth::getUserID(), $issue_id);
-+            }
-+            // record the change
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('duplicate_added'),
-+                    "Issue marked as a duplicate of issue #" . $_POST["duplicated_issue"] . " by " . User::getFullName(Auth::getUserID()));
-+            return 1;
-+        }
-+    }
-+
-+
-+    function isDuplicate($issue_id)
-+    {
-+        $sql = "SELECT
-+                    count(iss_id)
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id = " . Misc::escapeInteger($issue_id) . " AND
-+                    iss_duplicated_iss_id IS NULL";
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        }
-+        if ($res > 0) {
-+            return false;
-+        } else {
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get an associative array of user ID => user
-+     * status associated with a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of users
-+     */
-+    function getAssignedUsersStatus($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_id,
-+                    usr_status
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the summary associated with a given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  string The issue summary
-+     */
-+    function getTitle($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the issue ID associated with a specific summary.
-+     *
-+     * @access  public
-+     * @param   string $summary The summary to look for
-+     * @return  integer The issue ID
-+     */
-+    function getIssueID($summary)
-+    {
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_summary='" . Misc::escapeString($summary) . "'";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            if (empty($res)) {
-+                return 0;
-+            } else {
-+                return $res;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to add a new anonymous based issue in the system.
-+     *
-+     * @access  public
-+     * @return  integer The new issue ID
-+     */
-+    function addAnonymousReport()
-+    {
-+        $options = Project::getAnonymousPostOptions($_POST["project"]);
-+        $initial_status = Project::getInitialStatus($_POST["project"]);
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 (
-+                    iss_prj_id,
-+                    iss_prc_id,
-+                    iss_pre_id,
-+                    iss_pri_id,
-+                    iss_usr_id,";
-+        if (!empty($initial_status)) {
-+            $stmt .= "iss_sta_id,";
-+        }
-+        $stmt .= "
-+                    iss_created_date,
-+                    iss_last_public_action_date,
-+                    iss_last_public_action_type,
-+                    iss_summary,
-+                    iss_description,
-+                    iss_root_message_id
-+                 ) VALUES (
-+                    " . Misc::escapeInteger($_POST["project"]) . ",
-+                    " . $options["category"] . ",
-+                    0,
-+                    " . $options["priority"] . ",
-+                    " . $options["reporter"] . ",";
-+        if (!empty($initial_status)) {
-+            $stmt .= "$initial_status,";
-+        }
-+        $stmt .= "
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    'created',
-+                    '" . Misc::escapeString($_POST["summary"]) . "',
-+                    '" . Misc::escapeString($_POST["description"]) . "',
-+                    '" . Misc::escapeString(Mail_Helper::generateMessageID()) . "'
-+                 )";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return $res;
-+        } else {
-+            $new_issue_id = DB_Helper::get_last_insert_id();
-+            // log the creation of the issue
-+            History::add($new_issue_id, APP_SYSTEM_USER_ID, History::getTypeID('issue_opened_anon'), 'Issue opened anonymously');
-+
-+            // now process any files being uploaded
-+            $found = 0;
-+            for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+                if (!@empty($_FILES["file"]["name"][$i])) {
-+                    $found = 1;
-+                    break;
-+                }
-+            }
-+            if ($found) {
-+                $attachment_id = Attachment::add($new_issue_id, $options["reporter"], 'files uploaded anonymously');
-+                for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+                    $filename = @$_FILES["file"]["name"][$i];
-+                    if (empty($filename)) {
-+                        continue;
-+                    }
-+                    $blob = file_get_contents($_FILES["file"]["tmp_name"][$i]);
-+                    if (!empty($blob)) {
-+                        Attachment::addFile($attachment_id, $filename, $_FILES["file"]["type"][$i], $blob);
-+                    }
-+                }
-+            }
-+            // need to process any custom fields ?
-+            if (@count($_POST["custom_fields"]) > 0) {
-+                foreach ($_POST["custom_fields"] as $fld_id => $value) {
-+                    Custom_Field::associateIssue($new_issue_id, $fld_id, $value);
-+                }
-+            }
-+
-+            // now add the user/issue association
-+            $assign = array();
-+            $users = @$options["users"];
-+            $actions = Notification::getDefaultActions($new_issue_id, false, 'anon_issue');
-+            for ($i = 0; $i < count($users); $i++) {
-+                Notification::subscribeUser(APP_SYSTEM_USER_ID, $new_issue_id, $users[$i], $actions);
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $new_issue_id, $users[$i]);
-+                $assign[] = $users[$i];
-+            }
-+
-+            Workflow::handleNewIssue(Misc::escapeInteger($_POST["project"]),  $new_issue_id, false, false);
-+
-+            // also notify any users that want to receive emails anytime a new issue is created
-+            Notification::notifyNewIssue($_POST['project'], $new_issue_id);
-+
-+            return $new_issue_id;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove all issues associated with a specific list of
-+     * projects.
-+     *
-+     * @access  public
-+     * @param   array $ids The list of projects to look for
-+     * @return  boolean
-+     */
-+    function removeByProjects($ids)
-+    {
-+        $items = @implode(", ", Misc::escapeInteger($ids));
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id IN ($items)";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if (count($res) > 0) {
-+                self::deleteAssociations($res);
-+                Attachment::removeByIssues($res);
-+                SCM::removeByIssues($res);
-+                Impact_Analysis::removeByIssues($res);
-+                self::deleteUserAssociations($res);
-+                Note::removeByIssues($res);
-+                Time_Tracking::removeByIssues($res);
-+                Notification::removeByIssues($res);
-+                Custom_Field::removeByIssues($res);
-+                Phone_Support::removeByIssues($res);
-+                History::removeByIssues($res);
-+                // now really delete the issues
-+                $items = implode(", ", $res);
-+                $stmt = "DELETE FROM
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                         WHERE
-+                            iss_id IN ($items)";
-+                DB_Helper::getInstance()->query($stmt);
-+            }
-+            return true;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to close off an issue.
-+     *
-+     * @access  public
-+     * @param   integer $usr_id The user ID
-+     * @param   integer $issue_id The issue ID
-+     * @param   bool $send_notification Whether to send a notification about this action or not
-+     * @param   integer $resolution_id The resolution ID
-+     * @param   integer $status_id The status ID
-+     * @param   string $reason The reason for closing this issue
-+     * @param   string  $send_notification_to Who this notification should be sent too
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function close($usr_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason, $send_notification_to = 'internal')
-+    {
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $resolution_id = Misc::escapeInteger($resolution_id);
-+        $status_id = Misc::escapeInteger($status_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='closed',
-+                    iss_closed_date='" . Date_Helper::getCurrentDateGMT() . "',\n";
-+        if (!empty($resolution_id)) {
-+            $stmt .= "iss_res_id=$resolution_id,\n";
-+        }
-+        $stmt .= "iss_sta_id=$status_id
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            self::moveOrderForAllUsers($issue_id, 1000);
-+            $prj_id = self::getProjectID($issue_id);
-+
-+            // record the change
-+            History::add($issue_id, $usr_id, History::getTypeID('issue_closed'), "Issue updated to status '" . Status::getStatusTitle($status_id) . "' by " . User::getFullName($usr_id));
-+
-+            if ($send_notification_to == 'all') {
-+
-+                $from = User::getFromHeader($usr_id);
-+                $message_id = User::getFromHeader($usr_id);
-+                $full_email = Support::buildFullHeaders($issue_id, $message_id, $from,
-+                    '', '', 'Issue closed comments', $reason, '');
-+
-+                $structure = Mime_Helper::decode($full_email, true, false);
-+
-+                $email = array(
-+                    'ema_id'        =>  Email_Account::getEmailAccount(self::getProjectID($issue_id)),
-+                    'issue_id'      =>  $issue_id,
-+                    'message_id'    =>  $message_id,
-+                    'date'          =>  Date_Helper::getCurrentDateGMT(),
-+                    'subject'       =>  'Issue closed comments',
-+                    'from'          =>  $from,
-+                    'has_attachment'=>  0,
-+                    'body'          =>  $reason,
-+                    'full_email'    =>  $full_email,
-+                    'headers'       =>  $structure->headers
-+                );
-+                Support::insertEmail($email, $structure, $sup_id, true);
-+                $ids = $sup_id;
-+            } else {
-+                // add note with the reason to close the issue
-+                $_POST['title'] = 'Issue closed comments';
-+                $_POST["note"] = $reason;
-+                Note::insert($usr_id, $issue_id, false, true, true, $send_notification);
-+                $ids = false;
-+            }
-+
-+            if ($send_notification) {
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    // send a special confirmation email when customer issues are closed
-+                    $stmt = "SELECT
-+                                iss_customer_contact_id
-+                             FROM
-+                                " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                             WHERE
-+                                iss_id=$issue_id";
-+                    $customer_contact_id = DB_Helper::getInstance()->getOne($stmt);
-+                    if (!empty($customer_contact_id)) {
-+                        Customer::notifyIssueClosed($prj_id, $issue_id, $customer_contact_id, $send_notification, $resolution_id, $status_id, $reason);
-+                    }
-+                }
-+                // send notifications for the issue being closed
-+                Notification::notify($issue_id, 'closed', $ids);
-+            }
-+            Workflow::handleIssueClosed($prj_id, $issue_id, $send_notification, $resolution_id, $status_id, $reason);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to update the details of a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 or -2 otherwise
-+     */
-+    function update($issue_id)
-+    {
-+        global $errors;
-+        $errors = array();
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $usr_id = Auth::getUserID();
-+        $prj_id = self::getProjectID($issue_id);
-+
-+        $workflow = Workflow::preIssueUpdated($prj_id, $issue_id, $usr_id, $_POST);
-+        if ($workflow !== true) {
-+            return $workflow;
-+        }
-+
-+        // get all of the 'current' information of this issue
-+        $current = self::getDetails($issue_id);
-+        // update the issue associations
-+        if (empty($_POST['associated_issues'])) {
-+            $associated_issues = array();
-+        } else {
-+            $associated_issues = explode(',', @$_POST['associated_issues']);
-+            // make sure all associated issues are valid (and in this project)
-+            for ($i = 0; $i < count($associated_issues); $i++) {
-+                if (!self::exists(trim($associated_issues[$i]), false)) {
-+                    $errors['Associated Issues'][] = 'Issue #' . $associated_issues[$i] . ' does not exist and was removed from the list of associated issues.';
-+                    unset($associated_issues[$i]);
-+                }
-+            }
-+        }
-+        $association_diff = Misc::arrayDiff($current['associated_issues'], $associated_issues);
-+        if (count($association_diff) > 0) {
-+            // go through the new assocations, if association already exists, skip it
-+            $associations_to_remove = $current['associated_issues'];
-+            if (count($associated_issues) > 0) {
-+                foreach ($associated_issues as $index => $associated_id) {
-+                    if (!in_array($associated_id, $current['associated_issues'])) {
-+                        self::addAssociation($issue_id, $associated_id, $usr_id);
-+                    } else {
-+                        // already assigned, remove this user from list of users to remove
-+                        unset($associations_to_remove[array_search($associated_id, $associations_to_remove)]);
-+                    }
-+                }
-+            }
-+            if (count($associations_to_remove) > 0) {
-+                foreach ($associations_to_remove as $associated_id) {
-+                    self::deleteAssociation($issue_id, $associated_id);
-+                }
-+            }
-+        }
-+        $assignments_changed = false;
-+        if (@$_POST["keep_assignments"] == "no") {
-+            // only change the issue-user associations if there really were any changes
-+            $old_assignees = array_merge($current['assigned_users'], $current['assigned_inactive_users']);
-+            if (!empty($_POST['assignments'])) {
-+                $new_assignees = @$_POST['assignments'];
-+            } else {
-+                $new_assignees = array();
-+            }
-+            $assignment_notifications = array();
-+
-+            // remove people from the assignment list, if appropriate
-+            foreach ($old_assignees as $assignee) {
-+                if (!in_array($assignee, $new_assignees)) {
-+                    self::deleteUserAssociation($issue_id, $assignee);
-+                    $assignments_changed = true;
-+                }
-+            }
-+            // add people to the assignment list, if appropriate
-+            foreach ($new_assignees as $assignee) {
-+                if (!in_array($assignee, $old_assignees)) {
-+                    self::addUserAssociation($usr_id, $issue_id, $assignee);
-+                    Notification::subscribeUser($usr_id, $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'issue_update'), TRUE);
-+                    $assignment_notifications[] = $assignee;
-+                    $assignments_changed = true;
-+                }
-+            }
-+            if (count($assignment_notifications) > 0) {
-+                Notification::notifyNewAssignment($assignment_notifications, $issue_id);
-+            }
-+        }
-+        if (empty($_POST["estimated_dev_time"])) {
-+            $_POST["estimated_dev_time"] = 0;
-+        }
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='updated',";
-+        if (!empty($_POST["category"])) {
-+            $stmt .= "iss_prc_id=" . Misc::escapeInteger($_POST["category"]) . ",";
-+        }
-+        if (@$_POST["keep"] == "no") {
-+            $stmt .= "iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",";
-+        }
-+        if (!empty($_POST['expected_resolution_date'])) {
-+            $stmt .= "iss_expected_resolution_date='" . Misc::escapeString($_POST['expected_resolution_date']) . "',";
-+        } else {
-+            $stmt .= "iss_expected_resolution_date=null,";
-+        }
-+        $stmt .= "
-+                    iss_pre_id=" . Misc::escapeInteger($_POST["release"]) . ",
-+                    iss_pri_id=" . Misc::escapeInteger($_POST["priority"]) . ",
-+                    iss_sta_id=" . Misc::escapeInteger($_POST["status"]) . ",
-+                    iss_res_id=" . Misc::escapeInteger($_POST["resolution"]) . ",
-+                    iss_summary='" . Misc::escapeString($_POST["summary"]) . "',
-+                    iss_description='" . Misc::escapeString($_POST["description"]) . "',
-+                    iss_dev_time='" . Misc::escapeString($_POST["estimated_dev_time"]) . "',
-+                    iss_percent_complete= '" . Misc::escapeString($_POST["percent_complete"]) . "',
-+                    iss_trigger_reminders=" . Misc::escapeInteger($_POST["trigger_reminders"]) . ",
-+                    iss_grp_id ='" . Misc::escapeInteger($_POST["group"]) . "'";
-+        if (isset($_POST['private'])) {
-+            $stmt .= ",
-+                    iss_private = " . Misc::escapeInteger($_POST['private']);
-+        }
-+        $stmt .= "
-+                 WHERE
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // add change to the history (only for changes on specific fields?)
-+            $updated_fields = array();
-+            if ($current["iss_expected_resolution_date"] != $_POST['expected_resolution_date']) {
-+                $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $_POST['expected_resolution_date']);
-+            }
-+            if ($current["iss_prc_id"] != $_POST["category"]) {
-+                $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($_POST["category"]));
-+            }
-+            if ($current["iss_pre_id"] != $_POST["release"]) {
-+                $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($_POST["release"]));
-+            }
-+            if ($current["iss_pri_id"] != $_POST["priority"]) {
-+                $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($_POST["priority"]));
-+                Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $_POST);
-+            }
-+            if ($current["iss_sta_id"] != $_POST["status"]) {
-+                // clear out the last-triggered-reminder flag when changing the status of an issue
-+                Reminder_Action::clearLastTriggered($issue_id);
-+
-+                // if old status was closed and new status is not, clear closed data from issue.
-+                $old_status_details = Status::getDetails($current['iss_sta_id']);
-+                if ($old_status_details['sta_is_closed'] == 1) {
-+                    $new_status_details = Status::getDetails($_POST["status"]);
-+                    if ($new_status_details['sta_is_closed'] != 1) {
-+                        self::clearClosed($issue_id);
-+                    }
-+                }
-+                $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($_POST["status"]));
-+            }
-+            if ($current["iss_res_id"] != $_POST["resolution"]) {
-+                $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($_POST["resolution"]));
-+            }
-+            if ($current["iss_dev_time"] != $_POST["estimated_dev_time"]) {
-+                $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($_POST["estimated_dev_time"]*60)));
-+            }
-+            if ($current["iss_summary"] != $_POST["summary"]) {
-+                $updated_fields["Summary"] = '';
-+            }
-+            if ($current["iss_description"] != $_POST["description"]) {
-+                $updated_fields["Description"] = '';
-+            }
-+            if ((isset($_POST['private'])) && ($_POST['private'] != $current['iss_private'])) {
-+                $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($_POST['private']));
-+            }
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $i = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($i > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    if (($key != "Summary") && ($key != "Description")) {
-+                        $changes .= "$key: $value";
-+                    } else {
-+                        $changes .= "$key";
-+                    }
-+                    $i++;
-+                }
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
-+                // send notifications for the issue being updated
-+                Notification::notifyIssueUpdated($issue_id, $current, $_POST);
-+            }
-+
-+            // record group change as a seperate change
-+            if ($current["iss_grp_id"] != (int)$_POST["group"]) {
-+                History::add($issue_id, $usr_id, History::getTypeID('group_changed'),
-+                    "Group changed (" . History::formatChanges(Group::getName($current["iss_grp_id"]), Group::getName($_POST["group"])) . ") by " . User::getFullName($usr_id));
-+            }
-+
-+            // now update any duplicates, if any
-+            $update_dupe = array(
-+                'Category',
-+                'Release',
-+                'Priority',
-+                'Release',
-+                'Resolution'
-+            );
-+            // COMPAT: the following line requires PHP > 4.0.4
-+            $intersect = array_intersect($update_dupe, array_keys($updated_fields));
-+            if (($current["duplicates"] != '') && (count($intersect) > 0)) {
-+                self::updateDuplicates($issue_id);
-+            }
-+
-+            // if there is customer integration, mark last customer action
-+            if ((Customer::hasCustomerIntegration($prj_id)) && (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer'))) {
-+                self::recordLastCustomerAction($issue_id);
-+            }
-+
-+            if ($assignments_changed) {
-+                // XXX: we may want to also send the email notification for those "new" assignees
-+                Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, $usr_id, self::getDetails($issue_id), @$_POST['assignments'], false);
-+            }
-+
-+            Workflow::handleIssueUpdated($prj_id, $issue_id, $usr_id, $current, $_POST);
-+            // Move issue to another project
-+            if (isset($_POST['move_issue']) and (User::getRoleByUser($usr_id, $prj_id) >= User::getRoleID("Developer"))) {
-+                $new_prj_id = (int)@$_POST['new_prj'];
-+                if (($prj_id != $new_prj_id) && (array_key_exists($new_prj_id, Project::getAssocList($usr_id)))) {
-+                    if(User::getRoleByUser($usr_id, $new_prj_id) >= User::getRoleID("Reporter")) {
-+                        $res = self::moveIssue($issue_id, $new_prj_id);
-+                        if ($res == -1) {
-+                            return $res;
-+                        }
-+                    } else {
-+                        return -1;
-+                    }
-+                }
-+            }
-+            return 1;
-+        }
-+    }
-+
-+    /**
-+     * Method used to update the a single detail field of a specific issue.
-+     *
-+     * @param integer $issue_id
-+     * @param string $field_name
-+     * @param string $field_value
-+     * @param string $field_type string or integer (for escape)
-+     * @return integer 1 on success, -1 otherwise
-+     */
-+    function updateField($issue_id, $field_name, $filed_value) {
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        $usr_id = Auth::getUserID();
-+        $prj_id = self::getProjectID($issue_id);
-+
-+        // get all of the 'current' information of this issue
-+        $current = self::getDetails($issue_id);
-+
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='updated'";
-+
-+        switch ($field_name) {
-+            case 'category':
-+                $stmt .= ", iss_prc_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'release':
-+                $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'expected_resolution_date':
-+                if (is_null($filed_value)) {
-+                    $stmt .= ", iss_expected_resolution_date = null";
-+                } else {
-+                    $stmt .= ", iss_expected_resolution_date = '" . Misc::escapeString($filed_value) . "'";
-+                }
-+            break;
-+            case 'release':
-+                $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'priority':
-+                $stmt .= ", iss_pri_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'status':
-+                $stmt .= ", iss_sta_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'resolution':
-+                $stmt .= ", iss_res_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'summary':
-+                $stmt .= ", iss_summary = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'description':
-+                $stmt .= ", iss_description = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'estimated_dev_time':
-+                $stmt .= ", iss_dev_time = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'percent_complete':
-+                $stmt .= ", iss_percent_complete = '" . Misc::escapeString($filed_value) . "'";
-+            break;
-+            case 'trigger_reminders':
-+                $stmt .= ", iss_trigger_reminders = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'group':
-+                $stmt .= ", iss_grp_id = " . Misc::escapeInteger($filed_value);
-+            break;
-+            case 'private':
-+                $stmt .= ", iss_private = " . Misc::escapeInteger($filed_value);
-+            break;
-+            default:
-+                Error_Handler::logError("Unknown field name $field_name", __FILE__, __LINE__);
-+                return -1;
-+            break;
-+        }
-+
-+        $stmt .= "
-+                 WHERE
-+                    iss_id=$issue_id";
-+
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $new = array(
-+                'category' => $current['iss_prc_id'],
-+                'release' => $current['iss_pre_id'],
-+                'expected_resolution_date' => $current['iss_expected_resolution_date'],
-+                'release' => $current['iss_pre_id'],
-+                'priority' => $current['iss_pri_id'],
-+                'status' => $current['iss_sta_id'],
-+                'resolution' => $current['iss_res_id'],
-+                'summary' => $current['iss_summary'],
-+                'description' => $current['iss_description'],
-+                'estimated_dev_time' => $current['iss_dev_time'],
-+                'percent_complete' => $current['iss_percent_complete'],
-+                'trigger_reminders' => $current['iss_trigger_reminders'],
-+                'group' => $current['iss_grp_id'],
-+                'iss_private' => $current['private']
-+            );
-+            $new[$field_name] = $filed_value;
-+
-+            // add change to the history (only for changes on specific fields?)
-+            $updated_fields = array();
-+            if ($field_name == 'expected_resolution_date' && $current["iss_expected_resolution_date"] != $filed_value) {
-+                $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $filed_value);
-+            }
-+            if ($field_name == 'category' && $current["iss_prc_id"] != $filed_value) {
-+                $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($filed_value));
-+            }
-+            if ($field_name == 'release' && $current["iss_pre_id"] != $filed_value) {
-+                $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($filed_value));
-+            }
-+            if ($field_name == 'priority' && $current["iss_pri_id"] != $filed_value) {
-+                $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($filed_value));
-+                Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $new);
-+            }
-+            if ($field_name == 'status' && $current["iss_sta_id"] != $filed_value) {
-+                // clear out the last-triggered-reminder flag when changing the status of an issue
-+                Reminder_Action::clearLastTriggered($issue_id);
-+
-+                // if old status was closed and new status is not, clear closed data from issue.
-+                $old_status_details = Status::getDetails($current['iss_sta_id']);
-+                if ($old_status_details['sta_is_closed'] == 1) {
-+                    $new_status_details = Status::getDetails($filed_value);
-+                    if ($new_status_details['sta_is_closed'] != 1) {
-+                        self::clearClosed($issue_id);
-+                    }
-+                }
-+                $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($filed_value));
-+            }
-+            if ($field_name == 'resolution' && $current["iss_res_id"] != $filed_value) {
-+                $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($filed_value));
-+            }
-+            if ($field_name == 'estimated_dev_time' && $current["iss_dev_time"] != $filed_value) {
-+                $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($filed_value*60)));
-+            }
-+            if ($field_name == 'summary' && $current["iss_summary"] != $filed_value) {
-+                $updated_fields["Summary"] = '';
-+            }
-+            if ($field_name == 'description' && $current["iss_description"] != $filed_value) {
-+                $updated_fields["Description"] = '';
-+            }
-+            if ($field_name == 'private' && ($filed_value != $current['iss_private'])) {
-+                $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($filed_value));
-+            }
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $i = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($i > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    if (($key != "Summary") && ($key != "Description")) {
-+                        $changes .= "$key: $value";
-+                    } else {
-+                        $changes .= "$key";
-+                    }
-+                    $i++;
-+                }
-+
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
-+                // send notifications for the issue being updated
-+                Notification::notifyIssueUpdated($issue_id, $current, $new);
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Move the issue to a new project
-+     *
-+     * @param integer $issue_id
-+     * @param integer $new_prj_id
-+     * @return integer 1 on success, -1 otherwise
-+     */
-+    function moveIssue($issue_id, $new_prj_id)
-+    {
-+        $stmt = "UPDATE
-+              " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+          SET
-+              iss_prj_id = " . Misc::escapeInteger($new_prj_id) . "
-+          WHERE
-+              iss_id = " . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $currentDetails = self::getDetails($issue_id);
-+
-+            // set new category
-+            $new_iss_prc_list = Category::getAssocList($new_prj_id);
-+            $iss_prc_title = Category::getTitle($currentDetails['iss_prc_id']);
-+            $new_prc_id = array_search($iss_prc_title, $new_iss_prc_list);
-+            if ($new_prc_id === false) {
-+              // use the first category listed in the new project
-+              $new_prc_id = key($new_iss_prc_list);
-+            }
-+
-+            // set new priority
-+            $new_iss_pri_list = Priority::getAssocList($new_prj_id);
-+            $iss_pri_title = Priority::getTitle($currentDetails['iss_pri_id']);
-+            $new_pri_id = array_search($iss_pri_title, $new_iss_pri_list);
-+            if ($new_pri_id === false) {
-+              // use the first category listed in the new project
-+              $new_pri_id = key($new_iss_pri_list);
-+            }
-+
-+            // XXX: Set status if needed when moving issue
-+
-+            $stmt = "UPDATE
-+                  " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+              SET
-+                  iss_prc_id=" . Misc::escapeInteger($new_prc_id) . ",
-+                  iss_pri_id=" . $new_pri_id . "
-+              WHERE
-+                  iss_id=$issue_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            }
-+
-+            // clear project cache
-+            self::getProjectID($issue_id, true);
-+
-+            Notification::notifyNewIssue($new_prj_id, $issue_id);
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to associate an existing issue with another one.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $issue_id The other issue ID
-+     * @return  void
-+     */
-+    function addAssociation($issue_id, $associated_id, $usr_id, $link_issues = TRUE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $associated_id = Misc::escapeInteger($associated_id);
-+
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 (
-+                    isa_issue_id,
-+                    isa_associated_id
-+                 ) VALUES (
-+                    $issue_id,
-+                    $associated_id
-+                 )";
-+        DB_Helper::getInstance()->query($stmt);
-+        History::add($issue_id, $usr_id, History::getTypeID('issue_associated'), "Issue associated to #$associated_id by " . User::getFullName($usr_id));
-+        // link the associated issue back to this one
-+        if ($link_issues) {
-+            self::addAssociation($associated_id, $issue_id, $usr_id, FALSE);
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove the issue associations related to a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  void
-+     */
-+    function deleteAssociations($issue_id, $usr_id = FALSE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (is_array($issue_id)) {
-+            $issue_id = implode(", ", $issue_id);
-+        }
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 WHERE
-+                    isa_issue_id IN ($issue_id) OR
-+                    isa_associated_id IN ($issue_id)";
-+        DB_Helper::getInstance()->query($stmt);
-+        if ($usr_id) {
-+            History::add($issue_id, $usr_id, History::getTypeID('issue_all_unassociated'), 'Issue associations removed by ' . User::getFullName($usr_id));
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to remove a issue association from an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $associated_id The associated issue ID to remove.
-+     * @return  void
-+     */
-+    function deleteAssociation($issue_id, $associated_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $associated_id = Misc::escapeInteger($associated_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association
-+                 WHERE
-+                    (
-+                        isa_issue_id = $issue_id AND
-+                        isa_associated_id = $associated_id
-+                    ) OR
-+                    (
-+                        isa_issue_id = $associated_id AND
-+                        isa_associated_id = $issue_id
-+                    )";
-+        DB_Helper::getInstance()->query($stmt);
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_unassociated'),
-+                "Issue association #$associated_id removed by " . User::getFullName(Auth::getUserID()));
-+        History::add($associated_id, Auth::getUserID(), History::getTypeID('issue_unassociated'),
-+                "Issue association #$issue_id removed by " . User::getFullName(Auth::getUserID()));
-+    }
-+
-+
-+    /**
-+     * Method used to assign an issue with an user.
-+     *
-+     * @access  public
-+     * @param   integer $usr_id The user ID of the person performing this change
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $assignee_usr_id The user ID of the assignee
-+     * @param   boolean $add_history Whether to add a history entry about this or not
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function addUserAssociation($usr_id, $issue_id, $assignee_usr_id, $add_history = TRUE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $assignee_usr_id = Misc::escapeInteger($assignee_usr_id);
-+        $order = 1;
-+        // move all orders down to free "order space" for this new association
-+        $stmt = "UPDATE 
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 SET
-+                    isu_order = isu_order + 1
-+                 WHERE
-+                    isu_usr_id = $assignee_usr_id AND
-+                    isu_order >= $order";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        // insert the new association
-+        $stmt = "INSERT INTO
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 (
-+                    isu_iss_id,
-+                    isu_usr_id,
-+                    isu_assigned_date,
-+                    isu_order
-+                 ) VALUES (
-+                    $issue_id,
-+                    $assignee_usr_id,
-+                    '" . Date_Helper::getCurrentDateGMT() . "',
-+                    $order
-+                 )";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($add_history) {
-+                History::add($issue_id, $usr_id, History::getTypeID('user_associated'),
-+                    'Issue assigned to ' . User::getFullName($assignee_usr_id) . ' by ' . User::getFullName($usr_id));
-+            }
-+            return 1;
-+        }
-+    }
-+
-+    /**
-+     * Method used to get the order list to be rearranged
-+     * 
-+     * @access  private
-+     * @param   string $issue_id The issue ID or a comma seperated list of IDs already prepared for giving to mysql
-+     * @param   string $usr_id The user to remove. When not specified, all users are taken as to be removed for that issue
-+     * @return  mixed delete order list to be rearranged. Used as a parameter to the method of rearranging the order.
-+     */
-+    function getDeleteUserAssociationOrderList($issue_id, $usr_id = "")
-+    {
-+        // find all affected associantion orders
-+        $stmt = "SELECT isu_usr_id, isu_order FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                 isu_iss_id IN ($issue_id)";
-+        if ($usr_id !== FALSE) {
-+            $stmt.= " AND isu_usr_id IN ($usr_id)";
-+        }
-+        $stmt.= "ORDER BY isu_order";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            $deleted_orders = array();
-+            foreach ($res as $row) {
-+                if (empty($deleted_orders[$row['isu_usr_id']])) {
-+                    $deleted_orders[$row['isu_usr_id']] = array();
-+                }
-+                $deleted_orders[$row['isu_usr_id']] [] = $row['isu_order'];
-+            }
-+            return $deleted_orders;
-+        }
-+    }
-+
-+    /**
-+     *
-+     * Method used to rearrange order list in the db according to known deleted records
-+     *
-+     * @access  private
-+     * @param   mixed  deleteorder list
-+     * @return void
-+     */
-+    function rearrangeDeleteUserAssociationOrderList($delete_order_list)
-+    {
-+        if (empty($delete_order_list) || (!is_array($delete_order_list))) {
-+            return -1;
-+        }
-+        foreach ($delete_order_list as $isu_usr_id => $orders) {
-+            for ($i = 0; $i < count($orders); $i++) { // traverse all deleted orders
-+                // move the orders after them up to take the "order space" of the deleted records
-+                $stmt = "UPDATE
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                         SET
-+                            isu_order = isu_order - " . ($i+1) . "
-+                         WHERE
-+                            isu_usr_id = $isu_usr_id AND
-+                            isu_order > " . $orders[$i];
-+                if ($i < count($orders) - 1) {
-+                    $stmt.=  " AND
-+                            isu_order < " . $orders[$i+1];
-+                }
-+                $res = DB_Helper::getInstance()->query($stmt);
-+                if (PEAR::isError($res)) {
-+                    Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                    return -1;
-+                }
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Method used to delete all user assignments for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user ID of the person performing the change
-+     * @return  void
-+     */
-+    function deleteUserAssociations($issue_id, $usr_id = FALSE)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        if (is_array($issue_id)) {
-+            $issue_id = implode(", ", $issue_id);
-+        }
-+        $deleted_order_list = self::getDeleteUserAssociationOrderList($issue_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id IN ($issue_id)";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($usr_id) {
-+                History::add($issue_id, $usr_id, History::getTypeID('user_all_unassociated'), 'Issue assignments removed by ' . User::getFullName($usr_id));
-+            }
-+            self::rearrangeDeleteUserAsssociationOrderList($deleted_order_list);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to delete a single user assignments for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id The user to remove.
-+     * @param   boolean $add_history Whether to add a history entry about this or not
-+     * @return  void
-+     */
-+    function deleteUserAssociation($issue_id, $usr_id, $add_history = true)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $usr_id = Misc::escapeInteger($usr_id);
-+        $delete_order_list = self::getDeleteUserAssociationOrderList($issue_id, $usr_id);
-+        $stmt = "DELETE FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 WHERE
-+                    isu_iss_id = $issue_id AND
-+                    isu_usr_id = $usr_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            if ($add_history) {
-+                History::add($issue_id, Auth::getUserID(), History::getTypeID('user_unassociated'),
-+                    User::getFullName($usr_id) . ' removed from issue by ' . User::getFullName(Auth::getUserID()));
-+            }
-+            self::rearrangeDeleteUserAssociationOrderList($delete_order_list);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Creates an issue with the given email information.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @param   integer $usr_id The user responsible for this action
-+     * @param   string $sender The original sender of this email
-+     * @param   string $summary The issue summary
-+     * @param   string $description The issue description
-+     * @param   integer $category The category ID
-+     * @param   integer $priority The priority ID
-+     * @param   array $assignment The list of users to assign this issue to
-+     * @param   string $date The date the email was originally sent.
-+     * @param   string $msg_id The message ID of the email we are creating this issue from.
-+     * @return  void
-+     */
-+    function createFromEmail($prj_id, $usr_id, $sender, $summary, $description, $category, $priority, $assignment, $date, $msg_id)
-+    {
-+        $data = array();
-+        $exclude_list = array();
-+
-+        $sender_email = Mail_Helper::getEmailAddress($sender);
-+        $sender_usr_id = User::getUserIDByEmail($sender_email, true);
-+        if (!empty($sender_usr_id)) {
-+            $reporter = $sender_usr_id;
-+            $exclude_list[] = $sender_usr_id;
-+        }
-+
-+        $data = array(
-+            'category' => $category,
-+            'priority' => $priority,
-+            'description' => $description,
-+            'summary' => $summary,
-+            'msg_id' => $msg_id,
-+        );
-+
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            list($customer_id, $customer_contact_id) = Customer::getCustomerIDByEmails($prj_id, array($sender_email));
-+            if (!empty($customer_id)) {
-+                $contact = Customer::getContactDetails($prj_id, $customer_contact_id);
-+                // overwrite the reporter with the customer contact
-+                $reporter = User::getUserIDByContactID($customer_contact_id);
-+                $contact_timezone = Date_Helper::getPreferredTimezone($reporter);
-+
-+                $data['customer'] = $customer_id;
-+                $data['contact'] = $customer_contact_id;
-+#                $data['contract'] =  // XXX missing
-+                $data['contact_person_lname'] = $contact['last_name'];
-+                $data['contact_person_fname'] = $contact['first_name'];
-+                $data['contact_email'] = $sender_email;
-+                $data['contact_phone'] = $contact['phone'];
-+                $data['contact_timezone'] = $contact_timezone;
-+            }
-+        } else {
-+            $customer_id = FALSE;
-+        }
-+        if (empty($reporter)) {
-+            $reporter = APP_SYSTEM_USER_ID;
-+        }
-+
-+        $data['reporter'] = $reporter;
-+
-+        $issue_id = self::insertIssue($prj_id, $usr_id, $data);
-+        if ($issue_id == -1) {
-+            return -1;
-+        }
-+
-+        $has_TAM = false;
-+        $has_RR = false;
-+        // log the creation of the issue
-+        History::add($issue_id, $usr_id, History::getTypeID('issue_opened'), 'Issue opened by ' . $sender);
-+
-+        $emails = array();
-+        $manager_usr_ids = array();
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (!empty($customer_id))) {
-+            // if there are any technical account managers associated with this customer, add these users to the notification list
-+            $managers = Customer::getAccountManagers($prj_id, $customer_id);
-+            $manager_usr_ids = array_keys($managers);
-+            $manager_emails = array_values($managers);
-+            $emails = array_merge($emails, $manager_emails);
-+        }
-+        // add the reporter to the notification list
-+        $emails[] = $sender;
-+        $emails = array_unique($emails);
-+        $actions = Notification::getDefaultActions($issue_id, false, 'issue_from_email');
-+        foreach ($emails as $address) {
-+            Notification::subscribeEmail($reporter, $issue_id, $address, $actions);
-+        }
-+
-+        // only assign the issue to an user if the associated customer has any technical account managers
-+        $users = array();
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (count($manager_usr_ids) > 0)) {
-+            foreach ($manager_usr_ids as $manager_usr_id) {
-+                $users[] = $manager_usr_id;
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $manager_usr_id, false);
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)');
-+            }
-+            $has_TAM = true;
-+        }
-+        // now add the user/issue association
-+        if (@count($assignment) > 0) {
-+            for ($i = 0; $i < count($assignment); $i++) {
-+                Notification::subscribeUser($reporter, $issue_id, $assignment[$i], $actions);
-+                self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $assignment[$i]);
-+                if ($assignment[$i] != $usr_id) {
-+                    $users[] = $assignment[$i];
-+                }
-+            }
-+        } else {
-+            // only use the round-robin feature if this new issue was not
-+            // already assigned to a customer account manager
-+            if (@count($manager_usr_ids) < 1) {
-+                $assignee = Round_Robin::getNextAssignee($prj_id);
-+                // assign the issue to the round robin person
-+                if (!empty($assignee)) {
-+                    self::addUserAssociation(APP_SYSTEM_USER_ID, $issue_id, $assignee, false);
-+                    History::add($issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)');
-+                    $users[] = $assignee;
-+                    $has_RR = true;
-+                }
-+            }
-+        }
-+        if (count($users) > 0) {
-+            $has_assignee = true;
-+        }
-+
-+        Workflow::handleNewIssue($prj_id, $issue_id, $has_TAM, $has_RR);
-+
-+        // send special 'an issue was auto-created for you' notification back to the sender
-+        Notification::notifyAutoCreatedIssue($prj_id, $issue_id, $sender, $date, $summary);
-+
-+        // also notify any users that want to receive emails anytime a new issue is created
-+        Notification::notifyNewIssue($prj_id, $issue_id, $exclude_list);
-+
-+        return $issue_id;
-+    }
-+
-+
-+    /**
-+     * Return errors that happened when creating new issue from POST method.
-+     *
-+     * @return  array
-+     */
-+    private static $insert_errors = array();
-+    static function getInsertErrors() {
-+        return self::$insert_errors;
-+    }
-+
-+    /**
-+     * Method used to add a new issue using the normal report form.
-+     *
-+     * @access  public
-+     * @return  integer The new issue ID
-+     */
-+    function createFromPost()
-+    {
-+        $keys = array(
-+            'add_primary_contact', 'attached_emails', 'category', 'contact', 'contact_email', 'contact_extra_emails', 'contact_person_fname',
-+            'contact_person_lname', 'contact_phone', 'contact_timezone', 'contract', 'customer', 'custom_fields', 'description',
-+            'estimated_dev_time', 'group', 'notify_customer', 'notify_senders', 'priority', 'private', 'release', 'summary', 'users',
-+        );
-+        $data = array();
-+        foreach ($keys as $key) {
-+            if (isset($_POST[$key])) {
-+                $data[$key] = $_POST[$key];
-+            }
-+        }
-+
-+        $prj_id = Auth::getCurrentProject();
-+        $usr_id = Auth::getUserID();
-+
-+        // if we are creating an issue for a customer, put the
-+        // main customer contact as the reporter for it
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $contact_usr_id = User::getUserIDByContactID($data['contact']);
-+            if (empty($contact_usr_id)) {
-+                $contact_usr_id = $usr_id;
-+            }
-+            $data['reporter'] = $contact_usr_id;
-+        } else {
-+            $data['reporter'] = $usr_id;
-+        }
-+
-+        $data['msg_id'] = Mail_Helper::generateMessageID();
-+
-+        $issue_id = self::insertIssue($prj_id, $usr_id, $data);
-+        if ($issue_id == -1) {
-+            return -1;
-+        }
-+
-+        $has_TAM = false;
-+        $has_RR = false;
-+        $info = User::getNameEmail($usr_id);
-+        // log the creation of the issue
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_opened'), 'Issue opened by ' . User::getFullName(Auth::getUserID()));
-+
-+        $emails = array();
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            if (!empty($data['contact_extra_emails']) && count($data['contact_extra_emails']) > 0) {
-+                $emails = $data['contact_extra_emails'];
-+            }
-+            // add the primary contact to the notification list
-+            if ($data['add_primary_contact'] == 'yes') {
-+                $contact_email = User::getEmailByContactID($data['contact']);
-+                if (!empty($contact_email)) {
-+                    $emails[] = $contact_email;
-+                }
-+            }
-+            // if there are any technical account managers associated with this customer, add these users to the notification list
-+            $managers = Customer::getAccountManagers($prj_id, $data['customer']);
-+            $manager_usr_ids = array_keys($managers);
-+            $manager_emails = array_values($managers);
-+            $emails = array_merge($emails, $manager_emails);
-+        }
-+        // add the reporter to the notification list
-+        $emails[] = $info['usr_email'];
-+        $emails = array_unique($emails);
-+        foreach ($emails as $address) {
-+            Notification::subscribeEmail($usr_id, $issue_id, $address, Notification::getDefaultActions($issue_id, $address, 'new_issue'));
-+        }
-+
-+        // only assign the issue to an user if the associated customer has any technical account managers
-+        $users = array();
-+        $has_TAM = false;
-+        if ((Customer::hasCustomerIntegration($prj_id)) && (count($manager_usr_ids) > 0)) {
-+            foreach ($manager_usr_ids as $manager_usr_id) {
-+                $users[] = $manager_usr_id;
-+                self::addUserAssociation($usr_id, $issue_id, $manager_usr_id, false);
-+                History::add($issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)');
-+            }
-+            $has_TAM = true;
-+        }
-+        // now add the user/issue association (aka assignments)
-+        if (!empty($data['users']) && count($data['users']) > 0) {
-+            for ($i = 0; $i < count($data['users']); $i++) {
-+                Notification::subscribeUser($usr_id, $issue_id, $data['users'][$i],
-+                                Notification::getDefaultActions($issue_id, User::getEmail($data['users'][$i]), 'new_issue'));
-+                self::addUserAssociation($usr_id, $issue_id, $data['users'][$i]);
-+                if ($data['users'][$i] != $usr_id) {
-+                    $users[] = $data['users'][$i];
-+                }
-+            }
-+        } else {
-+            // only use the round-robin feature if this new issue was not
-+            // already assigned to a customer account manager
-+            if (@count($manager_usr_ids) < 1) {
-+                $assignee = Round_Robin::getNextAssignee($prj_id);
-+                // assign the issue to the round robin person
-+                if (!empty($assignee)) {
-+                    $users[] = $assignee;
-+                    self::addUserAssociation($usr_id, $issue_id, $assignee, false);
-+                    History::add($issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)');
-+                    $has_RR = true;
-+                }
-+            }
-+        }
-+
-+        // now process any files being uploaded
-+        $found = 0;
-+        for ($i = 0; $i < count(@$_FILES["file"]["name"]); $i++) {
-+            if (!@empty($_FILES["file"]["name"][$i])) {
-+                $found = 1;
-+                break;
-+            }
-+        }
-+        if ($found) {
-+            $files = array();
-+            for ($i = 0; $i < count($_FILES["file"]["name"]); $i++) {
-+                $filename = @$_FILES["file"]["name"][$i];
-+                if (empty($filename)) {
-+                    continue;
-+                }
-+                $blob = file_get_contents($_FILES["file"]["tmp_name"][$i]);
-+                if (empty($blob)) {
-+                    // error reading a file
-+                    self::$insert_errors["file[$i]"] = "There was an error uploading the file '$filename'.";
-+                    continue;
-+                }
-+                $files[] = array(
-+                    "filename" => $filename,
-+                    "type"     => $_FILES['file']['type'][$i],
-+                    "blob"     => $blob
-+                );
-+            }
-+            if (count($files) > 0) {
-+                $attachment_id = Attachment::add($issue_id, $usr_id, 'Files uploaded at issue creation time');
-+                foreach ($files as $file) {
-+                    Attachment::addFile($attachment_id, $file["filename"], $file["type"], $file["blob"]);
-+                }
-+            }
-+        }
-+        // need to associate any emails ?
-+        if (!empty($data['attached_emails'])) {
-+            $items = explode(",", $data['attached_emails']);
-+            Support::associate($usr_id, $issue_id, $items);
-+        }
-+        // need to notify any emails being converted into issues ?
-+        if (@count($data['notify_senders']) > 0) {
-+            $recipients = Notification::notifyEmailConvertedIntoIssue($prj_id, $issue_id, $data['notify_senders'], @$data['customer']);
-+        } else {
-+            $recipients = array();
-+        }
-+        // need to process any custom fields ?
-+        if (@count($data['custom_fields']) > 0) {
-+            foreach ($data['custom_fields'] as $fld_id => $value) {
-+                Custom_Field::associateIssue($issue_id, $fld_id, $value);
-+            }
-+        }
-+        // also send a special confirmation email to the customer contact
-+        if ((@$data['notify_customer'] == 'yes') && (!empty($data['contact']))) {
-+            // also need to pass the list of sender emails already notified,
-+            // so we can avoid notifying the same person again
-+            $contact_email = User::getEmailByContactID($data['contact']);
-+            if (@!in_array($contact_email, $recipients)) {
-+                Customer::notifyCustomerIssue($prj_id, $issue_id, $data['contact']);
-+            }
-+        }
-+
-+        Workflow::handleNewIssue($prj_id, $issue_id, $has_TAM, $has_RR);
-+
-+        // also notify any users that want to receive emails anytime a new issue is created
-+        Notification::notifyNewIssue($prj_id, $issue_id);
-+
-+        return $issue_id;
-+    }
-+
-+    /**
-+     * Insert issue to database.
-+     *
-+     * @param   integer $prj_id The project ID
-+     * @param   integer $usr_id The user responsible for this action
-+     * @param   array $data of issue to be inserted
-+     * @return  integer The new issue ID
-+     */
-+    private function insertIssue($prj_id, $usr_id, $data)
-+    {
-+
-+        // XXX missing_fields never used
-+        $missing_fields = array();
-+        if ($data['category'] == -1) {
-+            $missing_fields[] = 'Category';
-+        }
-+        if ($data['priority'] == -1) {
-+            $missing_fields[] = 'Priority';
-+        }
-+
-+        // if there is no reporter set, use the system user
-+        if (empty($data['reporter'])) {
-+              $data['reporter'] = APP_SYSTEM_USER_ID;
-+        }
-+
-+        if ((!isset($data['estimated_dev_time'])) || ($data['estimated_dev_time'] == '')) {
-+            $data['estimated_dev_time'] = 0;
-+        }
-+
-+        // add new issue
-+        $stmt = "INSERT INTO " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue ".
-+                "SET ".
-+                    "iss_prj_id=" . $prj_id . ",";
-+        if (!empty($data['group'])) {
-+            $stmt .= "iss_grp_id=" . Misc::escapeInteger($data['group']) . ",\n";
-+        }
-+        if (!empty($data['category'])) {
-+            $stmt .= "iss_prc_id=". Misc::escapeInteger($data['category']) . ",\n";
-+        }
-+        if (!empty($data['release'])) {
-+            $stmt .= "iss_pre_id=". Misc::escapeInteger($data['release']) . ",\n";
-+        }
-+        if (!empty($data['priority'])) {
-+            $stmt .= "iss_pri_id=". Misc::escapeInteger($data['priority']) . ",";
-+        }
-+
-+        $stmt .= "iss_usr_id=". Misc::escapeInteger($data['reporter']) .",";
-+
-+        $initial_status = Project::getInitialStatus($prj_id);
-+        if (!empty($initial_status)) {
-+            $stmt .= "iss_sta_id=" . Misc::escapeInteger($initial_status) . ",";
-+        }
-+
-+        if (Customer::hasCustomerIntegration($prj_id)) {
-+            $stmt .= "
-+                    iss_customer_id=". Misc::escapeInteger($data['customer']) . ",";
-+            if (!empty($data['contact'])) {
-+            $stmt .= "
-+                    iss_customer_contract_id='". Misc::escapeString($data['contract']) . "',";
-+            }
-+            $stmt .= "
-+                    iss_customer_contact_id=". Misc::escapeInteger($data['contact']) . ",
-+                    iss_contact_person_lname='". Misc::escapeString($data['contact_person_lname']) . "',
-+                    iss_contact_person_fname='". Misc::escapeString($data['contact_person_fname']) . "',
-+                    iss_contact_email='". Misc::escapeString($data['contact_email']) . "',
-+                    iss_contact_phone='". Misc::escapeString($data['contact_phone']) . "',
-+                    iss_contact_timezone='". Misc::escapeString($data['contact_timezone']) . "',";
-+        }
-+
-+        $stmt .= "
-+                    iss_created_date='". Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_public_action_type='created',
-+                    iss_summary='" . Misc::escapeString($data['summary']) . "',
-+                    iss_description='" . Misc::escapeString($data['description']) . "',
-+                    iss_dev_time='" . Misc::escapeString($data['estimated_dev_time']) . "',";
-+            if (!empty($data['contact'])) {
-+                $stmt .= "
-+                    iss_private=" . Misc::escapeInteger($data['private']) . " ,";
-+            }
-+        $stmt .= "
-+                    iss_root_message_id='". Misc::escapeString($data['msg_id']) ."'
-+        ";
-+
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+
-+        $issue_id = DB_Helper::get_last_insert_id();
-+        return $issue_id;
-+    }
-+
-+
-+    /**
-+     * Method used to get a specific parameter in the issue listing cookie.
-+     *
-+     * @access  public
-+     * @param   string $name The name of the parameter
-+     * @return  mixed The value of the specified parameter
-+     */
-+    function getParam($name)
-+    {
-+        $profile = Search_Profile::getProfile(Auth::getUserID(), Auth::getCurrentProject(), 'issue');
-+
-+        if (isset($_GET[$name])) {
-+            return $_GET[$name];
-+        } elseif (isset($_POST[$name])) {
-+            return $_POST[$name];
-+        } elseif (isset($profile[$name])) {
-+            return $profile[$name];
-+        } else {
-+            return "";
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to save the current search parameters in a cookie.
-+     *
-+     * @access  public
-+     * @return  array The search parameters
-+     */
-+    function saveSearchParams()
-+    {
-+        $sort_by = self::getParam('sort_by');
-+        $sort_order = self::getParam('sort_order');
-+        $users = self::getParam('users');
-+        if (empty($users) && ($sort_by == 'isu_order')) { // Sorting by isu_order is impossible when no user specified
-+            unset($sort_by);
-+            unset($sort_order);
-+        }
-+        $rows = self::getParam('rows');
-+        $hide_closed = self::getParam('hide_closed');
-+        if ($hide_closed === '') {
-+            $hide_closed = 1;
-+        }
-+        $search_type = self::getParam('search_type');
-+        if (empty($search_type)) {
-+            $search_type = 'all_text';
-+        }
-+        $custom_field = self::getParam('custom_field');
-+        if (is_string($custom_field)) {
-+            $custom_field = unserialize(urldecode($custom_field));
-+        }
-+        $cookie = array(
-+            'rows'           => $rows ? $rows : APP_DEFAULT_PAGER_SIZE,
-+            'pagerRow'       => self::getParam('pagerRow'),
-+            'hide_closed'    => $hide_closed,
-+            "sort_by"        => $sort_by ? $sort_by : "pri_rank",
-+            "sort_order"     => $sort_order ? $sort_order : "ASC",
-+            // quick filter form
-+            'keywords'       => self::getParam('keywords'),
-+            'search_type'    => $search_type,
-+            'users'          => self::getParam('users'),
-+            'status'         => self::getParam('status'),
-+            'priority'       => self::getParam('priority'),
-+            'category'       => self::getParam('category'),
-+            'customer_email' => self::getParam('customer_email'),
-+            // advanced search form
-+            'show_authorized_issues'        => self::getParam('show_authorized_issues'),
-+            'show_notification_list_issues' => self::getParam('show_notification_list_issues'),
-+            'reporter'       => self::getParam('reporter'),
-+            // other fields
-+            'release'        => self::getParam('release'),
-+            // custom fields
-+            'custom_field'   => $custom_field
-+        );
-+        // now do some magic to properly format the date fields
-+        $date_fields = array(
-+            'created_date',
-+            'updated_date',
-+            'last_response_date',
-+            'first_response_date',
-+            'closed_date'
-+        );
-+        foreach ($date_fields as $field_name) {
-+            $field = self::getParam($field_name);
-+            if (empty($field)) {
-+                continue;
-+            }
-+            if (@$field['filter_type'] == 'in_past') {
-+                @$cookie[$field_name] = array(
-+                    'filter_type'   =>  'in_past',
-+                    'time_period'   =>  $field['time_period']
-+                );
-+            } else {
-+                $end_field_name = $field_name . '_end';
-+                $end_field = self::getParam($end_field_name);
-+                @$cookie[$field_name] = array(
-+                    'past_hour'   => $field['past_hour'],
-+                    'Year'        => $field['Year'],
-+                    'Month'       => $field['Month'],
-+                    'Day'         => $field['Day'],
-+                    'start'       => $field['Year'] . '-' . $field['Month'] . '-' . $field['Day'],
-+                    'filter_type' => $field['filter_type'],
-+                    'end'         => $end_field['Year'] . '-' . $end_field['Month'] . '-' . $end_field['Day']
-+                );
-+                @$cookie[$end_field_name] = array(
-+                    'Year'        => $end_field['Year'],
-+                    'Month'       => $end_field['Month'],
-+                    'Day'         => $end_field['Day']
-+                );
-+            }
-+        }
-+        Search_Profile::save(Auth::getUserID(), Auth::getCurrentProject(), 'issue', $cookie);
-+        return $cookie;
-+    }
-+
-+
-+    /**
-+     * Method used to get the current sorting options used in the grid layout
-+     * of the issue listing page.
-+     *
-+     * @access  public
-+     * @param   array $options The current search parameters
-+     * @return  array The sorting options
-+     */
-+    function getSortingInfo($options)
-+    {
-+
-+        $custom_fields = Custom_Field::getFieldsToBeListed(Auth::getCurrentProject());
-+
-+        // default order for last action date, priority should be descending
-+        // for textual fields, like summary, ascending is reasonable
-+        $fields = array(
-+            "pri_rank" => "desc",
-+            "iss_id" => "desc",
-+            "iss_customer_id" => "desc",
-+            "prc_title" => "asc",
-+            "sta_rank" => "asc",
-+            "iss_created_date" => "desc",
-+            "iss_summary" => "asc",
-+            "last_action_date" => "desc",
-+            "usr_full_name" => "asc",
-+            "iss_expected_resolution_date" => "desc",
-+            "pre_title" => "asc",
-+            "assigned" => "asc",
-+            "isu_order" => "desc",
-+        );
-+
-+        foreach ($custom_fields as $fld_id => $fld_name) {
-+            $fields['custom_field_' . $fld_id] = "desc";
-+        }
-+
-+        $sortfields = array_combine(array_keys($fields), array_keys($fields));
-+        $sortfields["pre_title"] = "pre_scheduled_date";
-+        $sortfields["assigned"] = "isu_usr_id";
-+
-+        $items = array(
-+            "links"  => array(),
-+            "images" => array()
-+        );
-+        foreach ($sortfields as $field => $sortfield) {
-+            $sort_order = $fields[$field];
-+            if ($options["sort_by"] == $sortfield) {
-+                $items["images"][$field] = "images/" . strtolower($options["sort_order"]) . ".gif";
-+                if (strtolower($options["sort_order"]) == "asc") {
-+                    $sort_order = "desc";
-+                } else {
-+                    $sort_order = "asc";
-+                }
-+            }
-+            $items["links"][$field] = $_SERVER["PHP_SELF"] . "?sort_by=" . $sortfield . "&sort_order=" . $sort_order;
-+        }
-+        return $items;
-+    }
-+
-+
-+    /**
-+     * Returns the list of action date fields appropriate for the
-+     * current user ID.
-+     *
-+     * @access  public
-+     * @return  array The list of action date fields
-+     */
-+    function getLastActionFields()
-+    {
-+        $last_action_fields = array(
-+            "iss_last_public_action_date"
-+        );
-+        if (Auth::getCurrentRole() > User::getRoleID('Customer')) {
-+            $last_action_fields[] = "iss_last_internal_action_date";
-+        }
-+        if (count($last_action_fields) > 1) {
-+            return "GREATEST(" . implode(', IFNULL(', $last_action_fields) . ", '0000-00-00')) AS last_action_date";
-+        } else {
-+            return $last_action_fields[0] . " AS last_action_date";
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues to be displayed in the grid layout.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The current project ID
-+     * @param   array $options The search parameters
-+     * @param   integer $current_row The current page number
-+     * @param   integer $max The maximum number of rows per page
-+     * @return  array The list of issues to be displayed
-+     */
-+    function getListing($prj_id, $options, $current_row = 0, $max = 5)
-+    {
-+        if (strtoupper($max) == "ALL") {
-+            $max = 9999999;
-+        }
-+        $start = $current_row * $max;
-+        // get the current user's role
-+        $usr_id = Auth::getUserID();
-+        $role_id = User::getRoleByUser($usr_id, $prj_id);
-+
-+        // get any custom fields that should be displayed
-+        $custom_fields = Custom_Field::getFieldsToBeListed($prj_id);
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_grp_id,
-+                    iss_prj_id,
-+                    iss_sta_id,
-+                    iss_customer_id,
-+                    iss_customer_contract_id,
-+                    iss_created_date,
-+                    iss_updated_date,
-+                    iss_last_response_date,
-+                    iss_closed_date,
-+                    iss_last_customer_action_date,
-+                    iss_usr_id,
-+                    iss_summary,
-+                    pri_title,
-+                    prc_title,
-+                    sta_title,
-+                    sta_color status_color,
-+                    sta_id,
-+                    iqu_status,
-+                    grp_name `group`,
-+                    pre_title,
-+                    iss_last_public_action_date,
-+                    iss_last_public_action_type,
-+                    iss_last_internal_action_date,
-+                    iss_last_internal_action_type,
-+                    " . self::getLastActionFields() . ",
-+                    IF(iss_last_internal_action_date > iss_last_public_action_date, 'internal', 'public') AS action_type,
-+                    iss_private,
-+                    usr_full_name,
-+                    iss_percent_complete,
-+                    iss_dev_time,
-+                    iss_expected_resolution_date
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user";
-+        // join custom fields if we are searching by custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                if (($field['fld_type'] == 'date') && ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . '_' . $cfo_id . "\n";
-+                    }
-+                } else {
-+                    $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . "\n";
-+                }
-+            }
-+        }
-+        $stmt .= ")";
-+        // check for the custom fields we want to sort by
-+        if (strstr($options['sort_by'], 'custom_field') !== false) {
-+            $fld_id = str_replace("custom_field_", '', $options['sort_by']);
-+            $stmt .= "\n LEFT JOIN \n" .
-+                APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf_sort
-+                ON
-+                    (cf_sort.icf_iss_id = iss_id AND cf_sort.icf_fld_id = $fld_id) \n";
-+        }
-+        if (!empty($options["users"]) || $options["sort_by"] === "isu_usr_id") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id";
-+        }
-+        if ((!empty($options["show_authorized_issues"])) || (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id)))) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier
-+                 ON
-+                    iur_iss_id=iss_id";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
-+                 ON
-+                    sub_iss_id=iss_id";
-+        }
-+        $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . ".`" . APP_TABLE_PREFIX . "group`
-+                 ON
-+                    iss_grp_id=grp_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id=prc_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id = pre_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 ON
-+                    iss_id=iqu_iss_id AND
-+                    (iqu_expiration > '" . Date_Helper::getCurrentDateGMT() . "' OR iqu_expiration IS NULL)
-+                 WHERE
-+                    iss_prj_id= " . Misc::escapeInteger($prj_id);
-+        $stmt .= self::buildWhereClause($options);
-+
-+        if (strstr($options["sort_by"], 'custom_field') !== false) {
-+            $fld_details = Custom_Field::getDetails($fld_id);
-+            $sort_by = 'cf_sort.' . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']);
-+        } else {
-+            $sort_by = Misc::escapeString($options["sort_by"]);
-+        }
-+
-+        $stmt .= "
-+                 GROUP BY
-+                    iss_id
-+                 ORDER BY
-+                    " . $sort_by . " " . Misc::escapeString($options["sort_order"]) . ",
-+                    iss_id DESC";
-+        $total_rows = Pager::getTotalRows($stmt);
-+        $stmt .= "
-+                 LIMIT
-+                    " . Misc::escapeInteger($start) . ", " . Misc::escapeInteger($max);
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array(
-+                "list" => "",
-+                "info" => ""
-+            );
-+        } else {
-+            if (count($res) > 0) {
-+                self::getAssignedUsersByIssues($res);
-+                Time_Tracking::getTimeSpentByIssues($res);
-+                // need to get the customer titles for all of these issues...
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    Customer::getCustomerTitlesByIssues($prj_id, $res);
-+                    Customer::getSupportLevelsByIssues($prj_id, $res);
-+                }
-+                self::formatLastActionDates($res);
-+                self::getLastStatusChangeDates($prj_id, $res);
-+            } elseif ($current_row > 0) {
-+                // if there are no results, and the page is not the first page reset page to one and reload results
-+                Auth::redirect("list.php?pagerRow=0&rows=$max");
-+            }
-+            $groups = Group::getAssocList($prj_id);
-+            $categories = Category::getAssocList($prj_id);
-+            $column_headings = self::getColumnHeadings($prj_id);
-+            if (count($custom_fields) > 0) {
-+                $column_headings = array_merge($column_headings,$custom_fields);
-+            }
-+            $csv[] = @implode("\t", $column_headings);
-+            for ($i = 0; $i < count($res); $i++) {
-+                $res[$i]["time_spent"] = Misc::getFormattedTime($res[$i]["time_spent"]);
-+                $res[$i]["iss_created_date"] = Date_Helper::getFormattedDate($res[$i]["iss_created_date"]);
-+                $res[$i]["iss_expected_resolution_date"] = Date_Helper::getSimpleDate($res[$i]["iss_expected_resolution_date"], false);
-+                $fields = array(
-+                    $res[$i]['pri_title'],
-+                    $res[$i]['iss_id'],
-+                    $res[$i]['usr_full_name'],
-+                );
-+                // hide the group column from the output if no
-+                // groups are available in the database
-+                if (count($groups) > 0) {
-+                    $fields[] = $res[$i]['group'];
-+                }
-+                $fields[] = $res[$i]['assigned_users'];
-+                $fields[] = $res[$i]['time_spent'];
-+                // hide the category column from the output if no
-+                // categories are available in the database
-+                if (count($categories) > 0) {
-+                    $fields[] = $res[$i]['prc_title'];
-+                }
-+                if (Customer::hasCustomerIntegration($prj_id)) {
-+                    $fields[] = @$res[$i]['customer_title'];
-+                    // check if current user is acustomer and has a per incident contract.
-+                    // if so, check if issue is redeemed.
-+                    if (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer')) {
-+                        if ((Customer::hasPerIncidentContract($prj_id, self::getCustomerID($res[$i]['iss_id'])) &&
-+                                (Customer::isRedeemedIncident($prj_id, $res[$i]['iss_id'])))) {
-+                            $res[$i]['redeemed'] = true;
-+                        }
-+                    }
-+                }
-+                $fields[] = $res[$i]['sta_title'];
-+                $fields[] = $res[$i]["status_change_date"];
-+                $fields[] = $res[$i]["last_action_date"];
-+                $fields[] = $res[$i]['iss_dev_time'];
-+                $fields[] = $res[$i]['iss_summary'];
-+                $fields[] = $res[$i]['iss_expected_resolution_date'];
-+
-+                if (count($custom_fields) > 0) {
-+                    $res[$i]['custom_field'] = array();
-+                    $custom_field_values = Custom_Field::getListByIssue($prj_id, $res[$i]['iss_id']);
-+                    foreach ($custom_field_values as $this_field) {
-+                        if (!empty($custom_fields[$this_field['fld_id']])) {
-+                            $res[$i]['custom_field'][$this_field['fld_id']] = $this_field['value'];
-+                            $fields[] = $this_field['value'];
-+                        }
-+                    }
-+                }
-+
-+                $csv[] = @implode("\t", $fields);
-+            }
-+            $total_pages = ceil($total_rows / $max);
-+            $last_page = $total_pages - 1;
-+            return array(
-+                "list" => $res,
-+                "info" => array(
-+                    "current_page"  => $current_row,
-+                    "start_offset"  => $start,
-+                    "end_offset"    => $start + count($res),
-+                    "total_rows"    => $total_rows,
-+                    "total_pages"   => $total_pages,
-+                    "previous_page" => ($current_row == 0) ? "-1" : ($current_row - 1),
-+                    "next_page"     => ($current_row == $last_page) ? "-1" : ($current_row + 1),
-+                    "last_page"     => $last_page,
-+                    "custom_fields" => $custom_fields
-+                ),
-+                "csv" => @implode("\n", $csv)
-+            );
-+        }
-+    }
-+
-+
-+    /**
-+     * Processes a result set to format the "Last Action Date" column.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     */
-+    function formatLastActionDates(&$result)
-+    {
-+        for ($i = 0; $i < count($result); $i++) {
-+            if (($result[$i]['action_type'] == "internal") &&
-+                    (Auth::getCurrentRole() > User::getRoleID('Customer'))) {
-+                $label = $result[$i]["iss_last_internal_action_type"];
-+                $last_date = $result[$i]["iss_last_internal_action_date"];
-+            } else {
-+                $label = $result[$i]["iss_last_public_action_type"];
-+                $last_date = $result[$i]["iss_last_public_action_date"];
-+            }
-+            $date = new Date($last_date);
-+            $current = new Date(Date_Helper::getCurrentDateGMT());
-+            $result[$i]['last_action_date'] = sprintf("%s: %s ago", ucwords($label),
-+                    Date_Helper::getFormattedDateDiff($current->getDate(DATE_FORMAT_UNIXTIME), $date->getDate(DATE_FORMAT_UNIXTIME)));
-+        }
-+    }
-+
-+
-+    /**
-+     * Retrieves the last status change date for the given issue.
-+     *
-+     * @access  public
-+     * @param   integer $prj_id The project ID
-+     * @param   array $result The associative array of data
-+     * @see     self::getListing()
-+     */
-+    function getLastStatusChangeDates($prj_id, &$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_sta_id"];
-+        }
-+        if (count($ids) == 0) {
-+            return false;
-+        }
-+        $customizations = Status::getProjectStatusCustomization($prj_id, $ids);
-+        for ($i = 0; $i < count($result); $i++) {
-+            if (empty($result[$i]['iss_sta_id'])) {
-+                $result[$i]['status_change_date'] = '';
-+            } else {
-+                list($label, $date_field_name) = @$customizations[$result[$i]['iss_sta_id']];
-+                if ((empty($label)) || (empty($date_field_name))) {
-+                    $result[$i]['status_change_date'] = '';
-+                    continue;
-+                }
-+                $current = new Date(Date_Helper::getCurrentDateGMT());
-+                $desc = "$label: %s ago";
-+                $target_date = $result[$i][$date_field_name];
-+                if (empty($target_date)) {
-+                    $result[$i]['status_change_date'] = '';
-+                    continue;
-+                }
-+                $date = new Date($target_date);
-+                $result[$i]['status_change_date'] = sprintf($desc, Date_Helper::getFormattedDateDiff($current->getDate(DATE_FORMAT_UNIXTIME), $date->getDate(DATE_FORMAT_UNIXTIME)));
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues to be displayed in the grid layout.
-+     *
-+     * @access  public
-+     * @param   array $options The search parameters
-+     * @return  string The where clause
-+     */
-+    function buildWhereClause($options)
-+    {
-+        $usr_id = Auth::getUserID();
-+        $prj_id = Auth::getCurrentProject();
-+        $role_id = User::getRoleByUser($usr_id, $prj_id);
-+
-+        $stmt = ' AND iss_usr_id = usr_id';
-+        if ($role_id == User::getRoleID('Customer')) {
-+            $stmt .= " AND iss_customer_id=" . User::getCustomerID($usr_id);
-+        } elseif (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id))) {
-+            $stmt .= " AND (
-+                        iss_usr_id = $usr_id OR
-+                        iur_usr_id = $usr_id
-+                        )";
-+        }
-+
-+        if (!empty($options["users"])) {
-+            $stmt .= " AND (\n";
-+            if (stristr($options["users"], "grp") !== false) {
-+                $chunks = explode(":", $options["users"]);
-+                $stmt .= 'iss_grp_id = ' . Misc::escapeInteger($chunks[1]);
-+            } else {
-+                if ($options['users'] == '-1') {
-+                    $stmt .= 'isu_usr_id IS NULL';
-+                } elseif ($options['users'] == '-2') {
-+                    $stmt .= 'isu_usr_id IS NULL OR isu_usr_id=' . $usr_id;
-+                } elseif ($options['users'] == '-3') {
-+                    $stmt .= 'isu_usr_id = ' . $usr_id . ' OR iss_grp_id = ' . User::getGroupID($usr_id);
-+                } elseif ($options['users'] == '-4') {
-+                    $stmt .= 'isu_usr_id IS NULL OR isu_usr_id = ' . $usr_id . ' OR iss_grp_id = ' . User::getGroupID($usr_id);
-+                } else {
-+                    $stmt .= 'isu_usr_id =' . Misc::escapeInteger($options["users"]);
-+                }
-+            }
-+            $stmt .= ')';
-+        }
-+        if (!empty($options["reporter"])) {
-+            $stmt .= " AND iss_usr_id = " . Misc::escapeInteger($options["reporter"]);
-+        }
-+        if (!empty($options["show_authorized_issues"])) {
-+            $stmt .= " AND (iur_usr_id=$usr_id)";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= " AND (sub_usr_id=$usr_id)";
-+        }
-+        if (!empty($options["keywords"])) {
-+            $stmt .= " AND (\n";
-+            if (($options['search_type'] == 'all_text') && (APP_ENABLE_FULLTEXT)) {
-+                $stmt .= "iss_id IN(" . join(', ', self::getFullTextIssues($options)) . ")";
-+            } elseif (($options['search_type'] == 'customer') && (Customer::hasCustomerIntegration($prj_id))) {
-+                // check if the user is trying to search by customer email
-+                $customer_ids = Customer::getCustomerIDsLikeEmail($prj_id, $options['keywords']);
-+                if (count($customer_ids) > 0) {
-+                    $stmt .= " iss_customer_id IN (" . implode(', ', $customer_ids) . ")";
-+                } else {
-+                    // no results, kill query
-+                    $stmt .= " iss_customer_id = -1";
-+                }
-+            } else {
-+                $stmt .= "(" . Misc::prepareBooleanSearch('iss_summary', $options["keywords"]);
-+                $stmt .= " OR " . Misc::prepareBooleanSearch('iss_description', $options["keywords"]) . ")";
-+            }
-+            $stmt .= "\n) ";
-+        }
-+        if (!empty($options["priority"])) {
-+            $stmt .= " AND iss_pri_id=" . Misc::escapeInteger($options["priority"]);
-+        }
-+        if (!empty($options["status"])) {
-+            $stmt .= " AND iss_sta_id=" . Misc::escapeInteger($options["status"]);
-+        }
-+        if (!empty($options["category"])) {
-+            if (!is_array($options['category'])) {
-+                $options['category'] = array($options['category']);
-+            }
-+            $stmt .= " AND iss_prc_id IN(" . join(', ', Misc::escapeInteger($options["category"])) . ")";
-+        }
-+        if (!empty($options["hide_closed"])) {
-+            $stmt .= " AND sta_is_closed=0";
-+        }
-+        if (!empty($options['release'])) {
-+            $stmt .= " AND iss_pre_id = " . Misc::escapeInteger($options['release']);
-+        }
-+        // now for the date fields
-+        $date_fields = array(
-+            'created_date',
-+            'updated_date',
-+            'last_response_date',
-+            'first_response_date',
-+            'closed_date'
-+        );
-+        foreach ($date_fields as $field_name) {
-+            if (!empty($options[$field_name])) {
-+                switch ($options[$field_name]['filter_type']) {
-+                    case 'greater':
-+                        $stmt .= " AND iss_$field_name >= '" . Misc::escapeString($options[$field_name]['start']) . "'";
-+                        break;
-+                    case 'less':
-+                        $stmt .= " AND iss_$field_name <= '" . Misc::escapeString($options[$field_name]['start']) . "'";
-+                        break;
-+                    case 'between':
-+                        $stmt .= " AND iss_$field_name BETWEEN '" . Misc::escapeString($options[$field_name]['start']) . "' AND '" . Misc::escapeString($options[$field_name]['end']) . "'";
-+                        break;
-+                    case 'null':
-+                        $stmt .= " AND iss_$field_name IS NULL";
-+                        break;
-+                    case 'in_past':
-+                        if (strlen($options[$field_name]['time_period']) == 0) {
-+                            $options[$field_name]['time_period'] = 0;
-+                        }
-+                        $stmt .= " AND (UNIX_TIMESTAMP('" . Date_Helper::getCurrentDateGMT() . "') - UNIX_TIMESTAMP(iss_$field_name)) <= (" .
-+                            Misc::escapeInteger($options[$field_name]['time_period']) . "*3600)";
-+                        break;
-+                }
-+            }
-+        }
-+        // custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                $fld_db_name = Custom_Field::getDBValueFieldNameByType($field['fld_type']);
-+                if (($field['fld_type'] == 'date') &&
-+                        ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . ".icf_iss_id = iss_id";
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . ".icf_fld_id = $fld_id";
-+                        $stmt .= " AND\n cf" . $fld_id . '_' . $cfo_id . "." . $fld_db_name . " = $cfo_id";
-+                    }
-+                } elseif ($field['fld_type'] == 'date') {
-+                    if ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day']))) {
-+                        continue;
-+                    }
-+                    $search_value = $search_value['Year'] . "-" . $search_value['Month'] . "-" . $search_value['Day'];
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id AND
-+                        cf" . $fld_id . "." . $fld_db_name . " = '" . Misc::escapeString($search_value) . "')";
-+                } else if ($field['fld_type'] == 'integer') {
-+                    $value = $search_value['value'];
-+                    switch ($search_value['filter_type']) {
-+                        case 'ge': $cmp = '>='; break;
-+                        case 'le': $cmp = '<='; break;
-+                        case 'gt': $cmp = '>'; break;
-+                        case 'lt': $cmp = '<'; break;
-+                        default:   $cmp = '='; break;
-+                    }
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id";
-+                    $stmt .= " AND\n cf" . $fld_id . ".icf_fld_id = $fld_id";
-+                    $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . $cmp . Misc::escapeString($value) . ')';
-+                } else {
-+                    $stmt .= " AND\n (iss_id = cf" . $fld_id . ".icf_iss_id";
-+                    $stmt .= " AND\n cf" . $fld_id . ".icf_fld_id = $fld_id";
-+                    if ($field['fld_type'] == 'combo') {
-+                        $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . " IN(" . join(', ', Misc::escapeInteger($search_value)) . ")";
-+                    } else {
-+                        $stmt .= " AND cf" . $fld_id . "." . $fld_db_name . " LIKE '%" . Misc::escapeString($search_value) . "%'";
-+                    }
-+                    $stmt .= ')';
-+                }
-+            }
-+        }
-+        // clear cached full-text values if we are not searching fulltext anymore
-+        if ((APP_ENABLE_FULLTEXT) && (@$options['search_type'] != 'all_text')) {
-+            Session::set('fulltext_string', '');
-+            Session::set('fulltext_issues', '');
-+        }
-+        return $stmt;
-+    }
-+
-+
-+    /**
-+     * Method used to get the previous and next issues that are available
-+     * according to the current search parameters.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   array $options The search parameters
-+     * @return  array The list of issues
-+     */
-+    function getSides($issue_id, $options)
-+    {
-+        $usr_id = Auth::getUserID();
-+        $role_id = Auth::getCurrentRole();
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    " . self::getLastActionFields() . "
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user";
-+        // join custom fields if we are searching by custom fields
-+        if ((is_array($options['custom_field'])) && (count($options['custom_field']) > 0)) {
-+            foreach ($options['custom_field'] as $fld_id => $search_value) {
-+                if (empty($search_value)) {
-+                    continue;
-+                }
-+                $field = Custom_Field::getDetails($fld_id);
-+                if (($field['fld_type'] == 'date') &&
-+                        ((empty($search_value['Year'])) || (empty($search_value['Month'])) || (empty($search_value['Day'])))) {
-+                    continue;
-+                }
-+                if (($field['fld_type'] == 'integer') && empty($search_value['value'])) {
-+                    continue;
-+                }
-+
-+                if ($field['fld_type'] == 'multiple') {
-+                    $search_value = Misc::escapeInteger($search_value);
-+                    foreach ($search_value as $cfo_id) {
-+                        $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . '_' . $cfo_id . "\n";
-+                    }
-+                } else {
-+                    $stmt .= ",\n" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf" . $fld_id . "\n";
-+                }
-+            }
-+        }
-+        $stmt .= ")";
-+        // check for the custom fields we want to sort by
-+        if (strstr($options['sort_by'], 'custom_field') !== false) {
-+            $fld_id = str_replace("custom_field_", '', $options['sort_by']);
-+            $stmt .= "\n LEFT JOIN \n" .
-+                APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field as cf_sort
-+                ON
-+                    (icf_iss_id = iss_id AND icf_fld_id = $fld_id) \n";
-+        }
-+        if (!empty($options["users"]) || @$options["sort_by"] == "isu_usr_id") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                 ON
-+                    isu_iss_id=iss_id";
-+        }
-+        if ((!empty($options["show_authorized_issues"])) || (($role_id == User::getRoleID("Reporter")) && (Project::getSegregateReporters(Auth::getCurrentProject())))) {
-+             $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier
-+                 ON
-+                    iur_iss_id=iss_id";
-+        }
-+        if (!empty($options["show_notification_list_issues"])) {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
-+                 ON
-+                    sub_iss_id=iss_id";
-+        }
-+        if (@$options["sort_by"] == "pre_scheduled_date") {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id = pre_id";
-+        }
-+        if (@$options['sort_by'] == 'prc_title') {
-+            $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id = prc_id";
-+        }
-+        $stmt .= "
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        $stmt .= self::buildWhereClause($options);
-+        if (strstr($options["sort_by"], 'custom_field') !== false) {
-+            $fld_details = Custom_Field::getDetails($fld_id);
-+            $sort_by = 'cf_sort.' . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']);
-+        } else {
-+            $sort_by = Misc::escapeString($options["sort_by"]);
-+        }
-+        $stmt .= "
-+                 GROUP BY
-+                    iss_id
-+                 ORDER BY
-+                    " . $sort_by . " " . Misc::escapeString($options["sort_order"]) . ",
-+                    iss_id DESC";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            // COMPAT: the next line requires PHP >= 4.0.5
-+            $index = array_search($issue_id, $res);
-+            if (!empty($res[$index+1])) {
-+                $next = $res[$index+1];
-+            }
-+            if (!empty($res[$index-1])) {
-+                $previous = $res[$index-1];
-+            }
-+            return array(
-+                "next"     => @$next,
-+                "previous" => @$previous
-+            );
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of user IDs assigned to a specific
-+     * issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of user IDs
-+     */
-+    function getAssignedUserIDs($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to see if a user is assigned to an issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $usr_id An integer containg the ID of the user.
-+     * @return  boolean true if the user(s) are assigned to the issue.
-+     */
-+    function isAssignedToUser($issue_id, $usr_id)
-+    {
-+        $assigned_users = self::getAssignedUserIDs($issue_id);
-+        if (in_array($usr_id, $assigned_users)) {
-+            return true;
-+        } else {
-+            return false;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of reporters associated with a given
-+     * list of issues.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getReportersByIssues(&$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        $ids = implode(", ", $ids);
-+        $stmt = "SELECT
-+                    iss_id,
-+                    CONCAT(usr_full_name, ' <', usr_email, '>') AS usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    iss_usr_id=usr_id AND
-+                    iss_id IN ($ids)";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            // now populate the $result variable again
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['reporter'] = $res[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of assigned users by a list
-+     * of issues. This was originally created to optimize the issue
-+     * listing page.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getAssignedUsersByIssues(&$result)
-+    {
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        if (count($ids) < 1) {
-+            return;
-+        }
-+        $ids = implode(", ", $ids);
-+        $stmt = "SELECT
-+                    isu_iss_id,
-+                    isu_order,
-+                    isu_usr_id,
-+                    usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_usr_id=usr_id AND
-+                    isu_iss_id IN ($ids)";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            // gather names of the users assigned to each issue
-+            $t = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                if (!empty($t[$res[$i]['isu_iss_id']])) {
-+                    $t[$res[$i]['isu_iss_id']] .= ', ' . $res[$i]['usr_full_name'];
-+                } else {
-+                    $t[$res[$i]['isu_iss_id']] = $res[$i]['usr_full_name'];
-+                }
-+            }
-+            // gather orders
-+            $o = array();
-+            for ($i = 0; $i < count($res); $i++) {
-+                if (empty($o[$res[$i]['isu_iss_id']])) {
-+                    $o[$res[$i]['isu_iss_id']] = array();
-+                }
-+                $o[$res[$i]['isu_iss_id']][$res[$i]['isu_usr_id']] = $res[$i]['isu_order'];
-+            }
-+            // now populate the $result variable again
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['assigned_users'] = $t[$result[$i]['iss_id']];
-+                @$result[$i]['assigned_users_order'] = $o[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to add the issue description to a list of issues.
-+     *
-+     * @access  public
-+     * @param   array $result The result set
-+     * @return  void
-+     */
-+    function getDescriptionByIssues(&$result)
-+    {
-+        if (count($result) == 0) {
-+            return;
-+        }
-+
-+        $ids = array();
-+        for ($i = 0; $i < count($result); $i++) {
-+            $ids[] = $result[$i]["iss_id"];
-+        }
-+        $ids = implode(", ", $ids);
-+
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_description
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id in ($ids)";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+        } else {
-+            for ($i = 0; $i < count($result); $i++) {
-+                @$result[$i]['iss_description'] = $res[$result[$i]['iss_id']];
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of users (the full names) assigned to a
-+     * specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of users
-+     */
-+    function getAssignedUsers($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    usr_full_name
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                 WHERE
-+                    isu_iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    isu_usr_id=usr_id";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the details for a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   boolean $force_refresh If the cache should not be used.
-+     * @return  array The details for the specified issue
-+     */
-+    function getDetails($issue_id, $force_refresh = false)
-+    {
-+        static $returns;
-+
-+        $issue_id = Misc::escapeInteger($issue_id);
-+
-+        if (empty($issue_id)) {
-+            return '';
-+        }
-+
-+        if ((!empty($returns[$issue_id])) && ($force_refresh != true)) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue.*,
-+                    prj_title,
-+                    prc_title,
-+                    pre_title,
-+                    pri_title,
-+                    sta_title,
-+                    sta_abbreviation,
-+                    sta_color status_color,
-+                    sta_is_closed
-+                 FROM
-+                    (
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project
-+                    )
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority
-+                 ON
-+                    iss_pri_id=pri_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 ON
-+                    iss_sta_id=sta_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category
-+                 ON
-+                    iss_prc_id=prc_id
-+                 LEFT JOIN
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release
-+                 ON
-+                    iss_pre_id=pre_id
-+                 WHERE
-+                    iss_id=$issue_id AND
-+                    iss_prj_id=prj_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            if (empty($res)) {
-+                return "";
-+            } else {
-+                $created_date_ts = Date_Helper::getUnixTimestamp($res['iss_created_date'], Date_Helper::getDefaultTimezone());
-+                // get customer information, if any
-+                if ((!empty($res['iss_customer_id'])) && (Customer::hasCustomerIntegration($res['iss_prj_id']))) {
-+                    $res['customer_business_hours'] = Customer::getBusinessHours($res['iss_prj_id'], $res['iss_customer_id']);
-+                    $res['contact_local_time'] = Date_Helper::getFormattedDate(Date_Helper::getCurrentDateGMT(), $res['iss_contact_timezone']);
-+                    $res['customer_info'] = Customer::getDetails($res['iss_prj_id'], $res['iss_customer_id'], false, $res['iss_customer_contract_id']);
-+                    $res['redeemed_incidents'] = Customer::getRedeemedIncidentDetails($res['iss_prj_id'], $res['iss_id']);
-+                    $max_first_response_time = Customer::getMaximumFirstResponseTime($res['iss_prj_id'], $res['iss_customer_id'], $res['iss_customer_contract_id']);
-+                    $res['max_first_response_time'] = Misc::getFormattedTime($max_first_response_time / 60);
-+                    if (empty($res['iss_first_response_date'])) {
-+                        $first_response_deadline = $created_date_ts + $max_first_response_time;
-+                        if (Date_Helper::getCurrentUnixTimestampGMT() <= $first_response_deadline) {
-+                            $res['max_first_response_time_left'] = Date_Helper::getFormattedDateDiff($first_response_deadline, Date_Helper::getCurrentUnixTimestampGMT());
-+                        } else {
-+                            $res['overdue_first_response_time'] = Date_Helper::getFormattedDateDiff(Date_Helper::getCurrentUnixTimestampGMT(), $first_response_deadline);
-+                        }
-+                    }
-+                }
-+                $res['iss_original_description'] = $res["iss_description"];
-+                if (!strstr($_SERVER["PHP_SELF"], 'update.php')) {
-+                    $res["iss_description"] = nl2br(htmlspecialchars($res["iss_description"]));
-+                    $res["iss_resolution"] = Resolution::getTitle($res["iss_res_id"]);
-+                }
-+                $res["iss_impact_analysis"] = nl2br(htmlspecialchars($res["iss_impact_analysis"]));
-+                $res["iss_created_date"] = Date_Helper::getFormattedDate($res["iss_created_date"]);
-+                $res['iss_created_date_ts'] = $created_date_ts;
-+                $res["assignments"] = @implode(", ", array_values(self::getAssignedUsers($res["iss_id"])));
-+                list($res['authorized_names'], $res['authorized_repliers']) = Authorized_Replier::getAuthorizedRepliers($res["iss_id"]);
-+                $temp = self::getAssignedUsersStatus($res["iss_id"]);
-+                $res["has_inactive_users"] = 0;
-+                $res["assigned_users"] = array();
-+                $res["assigned_inactive_users"] = array();
-+                foreach ($temp as $usr_id => $usr_status) {
-+                    if (!User::isActiveStatus($usr_status)) {
-+                        $res["assigned_inactive_users"][] = $usr_id;
-+                        $res["has_inactive_users"] = 1;
-+                    } else {
-+                        $res["assigned_users"][] = $usr_id;
-+                    }
-+                }
-+                if (@in_array(Auth::getUserID(), $res["assigned_users"])) {
-+                    $res["is_current_user_assigned"] = 1;
-+                } else {
-+                    $res["is_current_user_assigned"] = 0;
-+                }
-+                $res["associated_issues_details"] = self::getAssociatedIssuesDetails($res["iss_id"]);
-+                $res["associated_issues"] = self::getAssociatedIssues($res["iss_id"]);
-+                $res["reporter"] = User::getFullName($res["iss_usr_id"]);
-+                if (empty($res["iss_updated_date"])) {
-+                    $res["iss_updated_date"] = 'not updated yet';
-+                } else {
-+                    $res["iss_updated_date"] = Date_Helper::getFormattedDate($res["iss_updated_date"]);
-+                }
-+                $res["estimated_formatted_time"] = Misc::getFormattedTime($res["iss_dev_time"]);
-+                if (Release::isAssignable($res["iss_pre_id"])) {
-+                    $release = Release::getDetails($res["iss_pre_id"]);
-+                    $res["pre_title"] = $release["pre_title"];
-+                    $res["pre_status"] = $release["pre_status"];
-+                }
-+                // need to return the list of issues that are duplicates of this one
-+                $res["duplicates"] = self::getDuplicateList($res["iss_id"]);
-+                $res["duplicates_details"] = self::getDuplicateDetailsList($res["iss_id"]);
-+                // also get the issue title of the duplicated issue
-+                if (!empty($res['iss_duplicated_iss_id'])) {
-+                    $res['duplicated_issue'] = self::getDuplicatedDetails($res['iss_duplicated_iss_id']);
-+                }
-+
-+                // get group information
-+                if (!empty($res["iss_grp_id"])) {
-+                    $res["group"] = Group::getDetails($res["iss_grp_id"]);
-+                }
-+
-+                // get quarantine issue
-+                $res["quarantine"] = self::getQuarantineInfo($res["iss_id"]);
-+
-+                $returns[$issue_id] = $res;
-+                return $res;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get some simple details about the given duplicated issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The duplicated issue details
-+     */
-+    function getDuplicatedDetails($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_summary title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_sta_id=sta_id AND
-+                    iss_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to bulk update a list of issues
-+     *
-+     * @access  public
-+     * @return  boolean
-+     */
-+    function bulkUpdate()
-+    {
-+        // check if user performing this chance has the proper role
-+        if (Auth::getCurrentRole() < User::getRoleID('Manager')) {
-+            return -1;
-+        }
-+
-+        $items = Misc::escapeInteger($_POST['item']);
-+        $new_status_id = Misc::escapeInteger($_POST['status']);
-+        $new_release_id = Misc::escapeInteger(@$_POST['release']);
-+        $new_priority_id = Misc::escapeInteger($_POST['priority']);
-+        $new_category_id = Misc::escapeInteger($_POST['category']);
-+
-+        for ($i = 0; $i < count($items); $i++) {
-+            if (!self::canAccess($items[$i], Auth::getUserID())) {
-+                continue;
-+            } elseif (self::getProjectID($_POST['item'][$i]) != Auth::getCurrentProject()) {
-+                // make sure issue is not in another project
-+                continue;
-+            }
-+
-+            $updated_fields = array();
-+
-+            // update assignment
-+            if (count(@$_POST['users']) > 0) {
-+                $users = Misc::escapeInteger($_POST['users']);
-+                // get who this issue is currently assigned too
-+                $stmt = "SELECT
-+                            isu_usr_id,
-+                            usr_full_name
-+                         FROM
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
-+                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
-+                         WHERE
-+                            isu_usr_id = usr_id AND
-+                            isu_iss_id = " . $items[$i];
-+                $current_assignees = DB_Helper::getInstance()->getAssoc($stmt);
-+                if (PEAR::isError($current_assignees)) {
-+                    Error_Handler::logError(array($current_assignees->getMessage(), $current_assignees->getDebugInfo()), __FILE__, __LINE__);
-+                    return -1;
-+                }
-+                foreach ($current_assignees as $usr_id => $usr_name) {
-+                    if (!in_array($usr_id, $users)) {
-+                        self::deleteUserAssociation($items[$i], $usr_id, false);
-+                    }
-+                }
-+                $new_user_names = array();
-+                $new_assignees = array();
-+                foreach ($users as $usr_id) {
-+                    $new_user_names[$usr_id] = User::getFullName($usr_id);
-+
-+                    // check if the issue is already assigned to this person
-+                    $stmt = "SELECT
-+                                COUNT(*) AS total
-+                             FROM
-+                                " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
-+                             WHERE
-+                                isu_iss_id=" . $items[$i] . " AND
-+                                isu_usr_id=" . $usr_id;
-+                    $total = DB_Helper::getInstance()->getOne($stmt);
-+                    if ($total > 0) {
-+                        continue;
-+                    } else {
-+                        $new_assignees[] = $usr_id;
-+                        // add the assignment
-+                        self::addUserAssociation(Auth::getUserID(), $items[$i], $usr_id, false);
-+                        Notification::subscribeUser(Auth::getUserID(), $items[$i], $usr_id, Notification::getAllActions());
-+                        Workflow::handleAssignment(Auth::getCurrentProject(), $items[$i], Auth::getUserID());
-+                    }
-+                }
-+                Notification::notifyNewAssignment($new_assignees, $items[$i]);
-+                $updated_fields['Assignment'] = History::formatChanges(join(', ', $current_assignees), join(', ', $new_user_names));
-+            }
-+
-+            // update status
-+            if (!empty($new_status_id)) {
-+                $old_status_id = self::getStatusID($items[$i]);
-+                $res = self::setStatus($items[$i], $new_status_id, false);
-+                if ($res == 1) {
-+                    $updated_fields['Status'] = History::formatChanges(Status::getStatusTitle($old_status_id), Status::getStatusTitle($new_status_id));
-+                }
-+            }
-+
-+            // update release
-+            if (!empty($new_release_id)) {
-+                $old_release_id = self::getRelease($items[$i]);
-+                $res = self::setRelease($items[$i], $new_release_id);
-+                if ($res == 1) {
-+                    $updated_fields['Release'] = History::formatChanges(Release::getTitle($old_release_id), Release::getTitle($new_release_id));
-+                }
-+            }
-+
-+            // update priority
-+            if (!empty($new_priority_id)) {
-+                $old_priority_id = self::getPriority($items[$i]);
-+                $res = self::setPriority($items[$i], $new_priority_id);
-+                if ($res == 1) {
-+                    $updated_fields['Priority'] = History::formatChanges(Priority::getTitle($old_priority_id), Priority::getTitle($new_priority_id));
-+                }
-+            }
-+
-+            // update category
-+            if (!empty($new_category_id)) {
-+                $old_category_id = self::getCategory($items[$i]);
-+                $res = self::setCategory($items[$i], $new_category_id);
-+                if ($res == 1) {
-+                    $updated_fields['Category'] = History::formatChanges(Category::getTitle($old_category_id), Category::getTitle($new_category_id));
-+                }
-+            }
-+
-+            if (count($updated_fields) > 0) {
-+                // log the changes
-+                $changes = '';
-+                $k = 0;
-+                foreach ($updated_fields as $key => $value) {
-+                    if ($k > 0) {
-+                        $changes .= "; ";
-+                    }
-+                    $changes .= "$key: $value";
-+                    $k++;
-+                }
-+                History::add($items[$i], Auth::getUserID(), History::getTypeID('issue_bulk_updated'), "Issue updated ($changes) by " . User::getFullName(Auth::getUserID()));
-+            }
-+
-+            // close if request
-+            if ((isset($_REQUEST['closed_status'])) && (!empty($_REQUEST['closed_status']))) {
-+                self::close(Auth::getUserID(), $items[$i], true, 0, Misc::escapeInteger($_REQUEST['closed_status']), Misc::escapeString($_REQUEST['closed_message']), $_REQUEST['notification_list']);
-+            }
-+        }
-+        return true;
-+    }
-+
-+
-+    /**
-+     * Method used to set the initial impact analysis for a specific issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer 1 if the update worked, -1 otherwise
-+     */
-+    function setImpactAnalysis($issue_id)
-+    {
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_date='" . Date_Helper::getCurrentDateGMT() . "',
-+                    iss_last_internal_action_type='update',
-+                    iss_developer_est_time=" . Misc::escapeInteger($_POST["dev_time"]) . ",
-+                    iss_impact_analysis='" . Misc::escapeString($_POST["impact_analysis"]) . "'
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        } else {
-+            // add the impact analysis to the history of the issue
-+            $summary = 'Initial Impact Analysis for issue set by ' . User::getFullName(Auth::getUserID());
-+            History::add($issue_id, Auth::getUserID(), History::getTypeID('impact_analysis_added'), $summary);
-+            return 1;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs that area available in the
-+     * system.
-+     *
-+     * @access  public
-+     * @param   string $extra_condition An extra condition in the WHERE clause
-+     * @return  array The list of issue IDs
-+     */
-+    function getColList($extra_condition = NULL)
-+    {
-+        $stmt = "SELECT
-+                    iss_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        if (!empty($extra_condition)) {
-+            $stmt .= " AND $extra_condition ";
-+        }
-+        $stmt .= "
-+                 ORDER BY
-+                    iss_id DESC";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the full list of issue IDs and their respective
-+     * titles.
-+     *
-+     * @access  public
-+     * @param   string $extra_condition An extra condition in the WHERE clause
-+     * @return  array The list of issues
-+     */
-+    function getAssocList($extra_condition = NULL)
-+    {
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_prj_id=" . Auth::getCurrentProject();
-+        if (!empty($extra_condition)) {
-+            $stmt .= " AND $extra_condition ";
-+        }
-+        $stmt .= "
-+                 ORDER BY
-+                    iss_id ASC";
-+        $res = DB_Helper::getInstance()->getAssoc($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return "";
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues associated to a specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of associated issues
-+     */
-+    function getAssociatedIssues($issue_id)
-+    {
-+        $issues = self::getAssociatedIssuesDetails($issue_id);
-+        $associated = array();
-+        for ($i = 0; $i < count($issues); $i++) {
-+            $associated[] = $issues[$i]['associated_issue'];
-+        }
-+        return $associated;
-+    }
-+
-+
-+    /**
-+     * Method used to get the list of issues associated details to a
-+     * specific issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  array The list of associated issues
-+     */
-+    function getAssociatedIssuesDetails($issue_id)
-+    {
-+        static $returns;
-+
-+        if (!empty($returns[$issue_id])) {
-+            return $returns[$issue_id];
-+        }
-+
-+        $stmt = "SELECT
-+                    isa_associated_id associated_issue,
-+                    iss_summary associated_title,
-+                    sta_title current_status,
-+                    sta_is_closed is_closed
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_association,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    isa_associated_id=iss_id AND
-+                    iss_sta_id=sta_id AND
-+                    isa_issue_id=$issue_id";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            $returns[$issue_id] = $res;
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method used to check whether an issue was already closed or not.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  boolean
-+     */
-+    function isClosed($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id) . " AND
-+                    iss_sta_id=sta_id AND
-+                    sta_is_closed=1";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            if ($res == 0) {
-+                return false;
-+            } else {
-+                return true;
-+            }
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns a simple list of issues that are currently set to some
-+     * form of quarantine. This is mainly used by the IRC interface.
-+     *
-+     * @access  public
-+     * @return  array List of quarantined issues
-+     */
-+    function getQuarantinedIssueList()
-+    {
-+        // XXX: would be nice to restrict the result list to only one project
-+        $stmt = "SELECT
-+                    iss_id,
-+                    iss_summary
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue,
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id=iss_id AND
-+                    iqu_expiration >= '" . Date_Helper::getCurrentDateGMT() . "' AND
-+                    iqu_expiration IS NOT NULL";
-+        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            self::getAssignedUsersByIssues($res);
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns the status of a quarantine.
-+     *
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer Indicates what the current state of quarantine is.
-+     */
-+    function getQuarantineInfo($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iqu_status,
-+                    iqu_expiration
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id = " . Misc::escapeInteger($issue_id) . " AND
-+                        (iqu_expiration > '" . Date_Helper::getCurrentDateGMT() . "' OR
-+                        iqu_expiration IS NULL)";
-+        $res = DB_Helper::getInstance()->getRow($stmt, DB_FETCHMODE_ASSOC);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array();
-+        } else {
-+            if (!empty($res["iqu_expiration"])) {
-+                $expiration_ts = Date_Helper::getUnixTimestamp($res['iqu_expiration'], Date_Helper::getDefaultTimezone());
-+                $res["time_till_expiration"] = Date_Helper::getFormattedDateDiff($expiration_ts, Date_Helper::getCurrentUnixTimestampGMT());
-+            }
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Sets the quarantine status. Optionally an expiration date can be set
-+     * to indicate when the quarantine expires. A status > 0 indicates that quarantine is active.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @param   integer $status The quarantine status
-+     * @param   string  $expiration The expiration date of quarantine (default empty)
-+     */
-+    function setQuarantine($issue_id, $status, $expiration = '')
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $status = Misc::escapeInteger($status);
-+
-+        // see if there is an existing record
-+        $stmt = "SELECT
-+                    COUNT(*)
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                 WHERE
-+                    iqu_iss_id = $issue_id";
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        if ($res > 0) {
-+            // update
-+            $stmt = "UPDATE
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                     SET
-+                        iqu_status = $status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\niqu_expiration = '" . Misc::escapeString($expiration) . "'";
-+            }
-+            $stmt .= "\nWHERE
-+                        iqu_iss_id = $issue_id";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            } else {
-+                // add history entry about this change taking place
-+                if ($status == 0) {
-+                    History::add($issue_id, Auth::getUserID(), History::getTypeID('issue_quarantine_removed'),
-+                            "Issue quarantine status cleared by " . User::getFullName(Auth::getUserID()));
-+                }
-+            }
-+        } else {
-+            // insert
-+            $stmt = "INSERT INTO
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_quarantine
-+                     (
-+                        iqu_iss_id,
-+                        iqu_status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\niqu_expiration\n";
-+            }
-+            $stmt .= ") VALUES (
-+                        $issue_id,
-+                        $status";
-+            if (!empty($expiration)) {
-+                $stmt .= ",\n'" . Misc::escapeString($expiration) . "'\n";
-+            }
-+            $stmt .= ")";
-+            $res = DB_Helper::getInstance()->query($stmt);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return -1;
-+            }
-+        }
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Sets the group of the issue.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @param   integer $group_id The ID of the group
-+     * @return  integer 1 if successful, -1 or -2 otherwise
-+     */
-+    function setGroup($issue_id, $group_id)
-+    {
-+        $issue_id = Misc::escapeInteger($issue_id);
-+        $group_id = Misc::escapeInteger($group_id);
-+
-+        $current = self::getDetails($issue_id);
-+        if ($current["iss_grp_id"] == $group_id) {
-+            return -2;
-+        }
-+        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 SET
-+                    iss_grp_id = $group_id
-+                 WHERE
-+                    iss_id = $issue_id";
-+        $res = DB_Helper::getInstance()->query($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return -1;
-+        }
-+        $current_user = Auth::getUserID();
-+        if (empty($current_user)) {
-+            $current_user = APP_SYSTEM_USER_ID;
-+        }
-+        History::add($issue_id, $current_user, History::getTypeID('group_changed'),
-+                "Group changed (" . History::formatChanges(Group::getName($current["iss_grp_id"]), Group::getName($group_id)) . ") by " . User::getFullName($current_user));
-+        return 1;
-+    }
-+
-+
-+    /**
-+     * Returns the group ID associated with the given issue ID.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The issue ID
-+     * @return  integer The associated group ID
-+     */
-+    function getGroupID($issue_id)
-+    {
-+        $stmt = "SELECT
-+                    iss_grp_id
-+                 FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return 0;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
-+    /**
-+     * Returns an array of issues based on full text search results.
-+     *
-+     * @param   array $options An array of search options
-+     * @return  array An array of issue IDS
-+     */
-+    function getFullTextIssues($options)
-+    {
-+        // check if a list of issues for this full text search is already cached
-+        $fulltext_string = Session::get('fulltext_string');
-+        if ((!empty($fulltext_string)) && ($fulltext_string == $options['keywords'])) {
-+            return Session::get('fulltext_issues');
-+        }
-+
-+        // no pre-existing list, generate them
-+        $stmt = "(SELECT
-+                    DISTINCT(iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                 WHERE
-+                     MATCH(iss_summary, iss_description) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(not_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "note
-+                 WHERE
-+                     MATCH(not_note) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(ttr_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking
-+                 WHERE
-+                     MATCH(ttr_summary) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                    DISTINCT(phs_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "phone_support
-+                 WHERE
-+                     MATCH(phs_description) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 ) UNION (
-+                 SELECT
-+                     DISTINCT(sup_iss_id)
-+                 FROM
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
-+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
-+                 WHERE
-+                     sup_id = seb_sup_id AND
-+                     MATCH(seb_body) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)
-+                 )";
-+        $res = DB_Helper::getInstance()->getCol($stmt);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return array(-1);
-+        } else {
-+            $stmt = "SELECT
-+                        DISTINCT(icf_iss_id)
-+                    FROM
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
-+                    WHERE
-+                        MATCH (icf_value) AGAINST ('" . Misc::escapeString($options['keywords']) . "' IN BOOLEAN MODE)";
-+            $custom_res = DB_Helper::getInstance()->getCol($stmt);
-+            if (PEAR::isError($custom_res)) {
-+                Error_Handler::logError(array($custom_res->getMessage(), $custom_res->getDebugInfo()), __FILE__, __LINE__);
-+                return array(-1);
-+            }
-+            $issues = array_merge($res, $custom_res);
-+            // we kill the query results on purpose to flag that no
-+            // issues could be found with fulltext search
-+            if (count($issues) < 1) {
-+                $issues = array(-1);
-+            }
-+            Session::set('fulltext_string', $options['keywords']);
-+            Session::set('fulltext_issues', $issues);
-+            return $issues;
-+        }
-+    }
-+
-+
-+    /**
-+     * Method to determine if user can access a particular issue
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue.
-+     * @param   integer $usr_id The ID of the user
-+     * @return  boolean If the user can access the issue
-+     */
-+    function canAccess($issue_id, $usr_id)
-+    {
-+        static $access;
-+
-+        if (empty($issue_id)) {
-+            return true;
-+        }
-+
-+        if (isset($access[$issue_id . "-" . $usr_id])) {
-+            return $access[$issue_id . "-" . $usr_id];
-+        }
-+
-+        $details = self::getDetails($issue_id);
-+        if (empty($details)) {
-+            return true;
-+        }
-+        $usr_details = User::getDetails($usr_id);
-+        $usr_role = User::getRoleByUser($usr_id, $details['iss_prj_id']);
-+        $prj_id = self::getProjectID($issue_id);
-+
-+
-+        if (empty($usr_role)) {
-+            // check if they are even allowed to access the project
-+            $return = false;
-+        } elseif ((Customer::hasCustomerIntegration($details['iss_prj_id'])) && ($usr_role == User::getRoleID("Customer")) &&
-+                ($details['iss_customer_id'] != $usr_details['usr_customer_id'])) {
-+            // check customer permissions
-+            $return = false;
-+        } elseif ($details['iss_private'] == 1) {
-+            // check if the issue is even private
-+
-+            // check role, reporter, assigment and group
-+            if ($usr_role > User::getRoleID("Developer")) {
-+                $return = true;
-+            } elseif ($details['iss_usr_id'] == $usr_id) {
-+                $return = true;
-+            } elseif (self::isAssignedToUser($issue_id, $usr_id)) {
-+                $return = true;
-+            } elseif ((!empty($details['iss_grp_id'])) && (!empty($usr_details['usr_grp_id'])) &&
-+                        ($details['iss_grp_id'] == $usr_details['usr_grp_id'])) {
-+                $return = true;
-+            } elseif (Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id)) {
-+                $return = true;
-+            } else {
-+                $return = false;
-+            }
-+        } elseif ((Auth::getCurrentRole() == User::getRoleID("Reporter")) && (Project::getSegregateReporters($prj_id)) &&
-+                ($details['iss_usr_id'] != $usr_id) && (!Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id))) {
-+            return false;
-+        } else {
-+            $return = true;
++            result += tableId + '[]=' + rowId;
 +        }
++        return result;
++      },
 +
-+        $access[$issue_id . "-" . $usr_id] = $return;
-+        return $return;
++      serializeTables: function() {
++        var result = "";
++        this.each(function() {
++                      // this is now bound to each matching table
++                      result += jQuery.tableDnD.serializeTable(this);
++              });
++        return result;
 +    }
 +
++}
 +
-+    /**
-+     * Returns true if the specified issue is private, false otherwise
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     * @return  boolean If the issue is private or not
-+     */
-+    function isPrivate($issue_id)
-+    {
-+        static $returns;
++jQuery.fn.extend(
++      {
++              tableDnD : jQuery.tableDnD.build,
++              tableDnDUpdate : jQuery.tableDnD.updateTables,
++              tableDnDSerialize: jQuery.tableDnD.serializeTables
++      }
++);
+\ No newline at end of file
+--- eventum-2.3.2/htdocs/list.php~     2012-03-09 18:19:56.000000000 +0200
++++ eventum-2.3.2/htdocs/list.php      2012-03-09 18:32:43.998284397 +0200
+@@ -67,6 +67,11 @@
+     }
+ }
++@$reorder_usr_id = $_REQUEST["reorder_user"];
++@$reorder_issue_id = $_REQUEST["reorder_source"];
++@$reorder_neworder = $_REQUEST["reorder_neworder"];
++Issue::reorderUserIssues($reorder_usr_id, $reorder_issue_id, $reorder_neworder);
 +
-+        if (!isset($returns[$issue_id])) {
-+            $sql = "SELECT
-+                        iss_private
-+                    FROM
-+                        " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                    WHERE
-+                        iss_id=$issue_id";
-+            $res = DB_Helper::getInstance()->getOne($sql);
-+            if (PEAR::isError($res)) {
-+                Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+                return true;
-+            } else {
-+                if ($res == 1) {
-+                    $returns[$issue_id] = true;
-+                } else {
-+                    $returns[$issue_id] = false;
-+                }
-+            }
-+        }
-+        return $returns[$issue_id];
+ if (!empty($_REQUEST['nosave'])) {
+       $options = Search::saveSearchParams(false);
+ } else {
+@@ -92,6 +97,24 @@
+ }
+ $assign_options += $users;
++// get the isu_order (assigned users) ordering user
++if (!empty($options["users"])) {
++    if ($options["users"] == -2) {
++        $isu_order_user = $usr_id;
++    } else
++    if ($options["users"] > 0) {
++        $isu_order_user = $options["users"];
++    } else {
++        unset($isu_order_user);
 +    }
++} else {
++    unset($isu_order_user);
++}
 +
++if (isset($isu_order_user)) {
++    $tpl->assign("isu_order_user", $isu_order_user);
++}
 +
-+    /**
-+     * Clears closed information from an issues.
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     */
-+    function clearClosed($issue_id)
-+    {
+ $list = Search::getListing($prj_id, $options, $pagerRow, $rows);
+ $tpl->assign("list", $list["list"]);
+ $tpl->assign("list_info", $list["info"]);
+--- eventum-2.2/lib/eventum/class.display_column.php   2009-09-14 18:07:55.000000000 +0300
++++ eventum-2.2-order/lib/eventum/class.display_column.php     2009-10-12 22:10:36.429185594 +0300
+@@ -230,7 +230,10 @@
+                 ),
+                 "iss_expected_resolution_date"  =>  array(
+                     "title" =>  ev_gettext("Expected Resolution Date")
+-                )
++                ),
++                "isu_order" => array(
++                    "title" => ev_gettext("Order")
++                ),
+             )
+         );
+         return $columns[$page];
+--- eventum-2.3.1/lib/eventum/class.issue.php~ 2011-09-15 09:36:55.000000000 +0300
++++ eventum-2.3.1/lib/eventum/class.issue.php  2011-09-15 09:42:02.844032474 +0300
+@@ -1374,6 +1374,7 @@
+             return -1;
+         }
++        self::moveOrderForAllUsers($issue_id, 1000);
+         $prj_id = self::getProjectID($issue_id);
+         // record the change
+@@ -1800,16 +1801,33 @@
+     {
+         $issue_id = Misc::escapeInteger($issue_id);
+         $assignee_usr_id = Misc::escapeInteger($assignee_usr_id);
++        $order = 1;
++        // move all orders down to free "order space" for this new association
 +        $stmt = "UPDATE
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
++                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
 +                 SET
-+                    iss_closed_date = null,
-+                    iss_res_id = null
++                    isu_order = isu_order + 1
 +                 WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
++                    isu_usr_id = $assignee_usr_id AND
++                    isu_order >= $order";
 +        $res = DB_Helper::getInstance()->query($stmt);
 +        if (PEAR::isError($res)) {
 +            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 +            return -1;
 +        }
-+        self::moveOrderForAllUsers($issue_id, 1);
-+    }
-+
-+
-+    /**
-+     * Returns the message ID that should be used as the parent ID for all messages
-+     *
-+     * @access  public
-+     * @param   integer $issue_id The ID of the issue
-+     */
-+    function getRootMessageID($issue_id)
-+    {
-+        $sql = "SELECT
-+                    iss_root_message_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_id=" . Misc::escapeInteger($issue_id);
-+        $res = DB_Helper::getInstance()->getOne($sql);
-+        if (PEAR::isError($res)) {
-+            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        } else {
-+            return $res;
-+        }
-+    }
-+
-+
++        // insert the new association
+         $stmt = "INSERT INTO
+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
+                  (
+                     isu_iss_id,
+                     isu_usr_id,
+-                    isu_assigned_date
++                    isu_assigned_date,
++                    isu_order
+                  ) VALUES (
+                     $issue_id,
+                     $assignee_usr_id,
+-                    '" . Date_Helper::getCurrentDateGMT() . "'
++                    '" . Date_Helper::getCurrentDateGMT() . "',
++                    $order
+                  )";
+         $res = DB_Helper::getInstance()->query($stmt);
+         if (PEAR::isError($res)) {
+@@ -1824,6 +1842,78 @@
+         }
+     }
 +    /**
-+     * Returns the issue ID of the issue with the specified root message ID, or false
-+     * @access  public
-+     * @param   string $msg_id The Message ID
-+     * @return  integer The ID of the issue
++     * Method used to get the order list to be rearranged
++     * 
++     * @access  private
++     * @param   string $issue_id The issue ID or a comma seperated list of IDs already prepared for giving to mysql
++     * @param   string $usr_id The user to remove. When not specified, all users are taken as to be removed for that issue
++     * @return  mixed delete order list to be rearranged. Used as a parameter to the method of rearranging the order.
 +     */
-+    function getIssueByRootMessageID($msg_id)
++    function getDeleteUserAssociationOrderList($issue_id, $usr_id = "")
 +    {
-+        static $returns;
-+
-+        if (!empty($returns[$msg_id])) {
-+            return $returns[$msg_id];
++        // find all affected associantion orders
++        $stmt = "SELECT isu_usr_id, isu_order FROM
++                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
++                 WHERE
++                 isu_iss_id IN ($issue_id)";
++        if (!empty($usr_id)) {
++            $stmt.= " AND isu_usr_id IN ($usr_id)";
 +        }
-+        $sql = "SELECT
-+                    iss_id
-+                FROM
-+                    " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
-+                WHERE
-+                    iss_root_message_id = '" . Misc::escapeString($msg_id) . "'";
-+        $res = DB_Helper::getInstance()->getOne($sql);
++        $stmt.= "ORDER BY isu_order";
++        $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
 +        if (PEAR::isError($res)) {
 +            Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
-+            return false;
-+        }
-+        if (empty($res)) {
-+            $returns[$msg_id] = false;
++            return -1;
 +        } else {
-+            $returns[$msg_id] =  $res;
++            $deleted_orders = array();
++            foreach ($res as $row) {
++                if (empty($deleted_orders[$row['isu_usr_id']])) {
++                    $deleted_orders[$row['isu_usr_id']] = array();
++                }
++                $deleted_orders[$row['isu_usr_id']] [] = $row['isu_order'];
++            }
++            return $deleted_orders;
 +        }
-+        return $returns[$msg_id];
 +    }
 +
-+
 +    /**
-+     * Sets the assignees for the issue
 +     *
-+     * @param   integer $issue_id
-+     * @param   array   $assignees
++     * Method used to rearrange order list in the db according to known deleted records
++     *
++     * @access  private
++     * @param   mixed  deleteorder list
++     * @return void
 +     */
-+    function setAssignees($issue_id, $assignees)
++    function rearrangeDeleteUserAssociationOrderList($delete_order_list)
 +    {
-+        if (!is_array($assignees)) {
-+            $assignees = array();
-+        }
-+
-+        // see if there is anything to change
-+        $old_assignees = self::getAssignedUserIDs($issue_id);
-+        if ((count(array_diff($old_assignees, $assignees)) == 0) && (count(array_diff($assignees, $old_assignees)) == 0)) {
-+            return;
++        if (empty($delete_order_list) || (!is_array($delete_order_list))) {
++            return -1;
 +        }
-+
-+        $old_assignee_names = self::getAssignedUsers($issue_id);
-+
-+        Workflow::handleAssignmentChange(self::getProjectID($issue_id), $issue_id, Auth::getUserID(), self::getDetails($issue_id), $assignees, true);
-+        // clear up the assignments for this issue, and then assign it to the current user
-+        self::deleteUserAssociations($issue_id);
-+        $assignee_names = array();
-+        foreach ($assignees as $assignee) {
-+            $res = self::addUserAssociation(Auth::getUserID(), $issue_id, $assignee, false);
-+            if ($res == -1) {
-+                return false;
++        foreach ($delete_order_list as $isu_usr_id => $orders) {
++            for ($i = 0; $i < count($orders); $i++) { // traverse all deleted orders
++                // move the orders after them up to take the "order space" of the deleted records
++                $stmt = "UPDATE
++                            " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
++                         SET
++                            isu_order = isu_order - " . ($i+1) . "
++                         WHERE
++                            isu_usr_id = $isu_usr_id AND
++                            isu_order > " . $orders[$i];
++                if ($i < count($orders) - 1) {
++                    $stmt.=  " AND
++                            isu_order < " . $orders[$i+1];
++                }
++                $res = DB_Helper::getInstance()->query($stmt);
++                if (PEAR::isError($res)) {
++                    Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
++                    return -1;
++                }
 +            }
-+            $assignee_names[] = User::getFullName($assignee);
-+            Notification::subscribeUser(Auth::getUserID(), $issue_id, $assignee, Notification::getDefaultActions($issue_id, User::getEmail($assignee), 'set_assignees'), false);
 +        }
-+
-+        Notification::notifyNewAssignment($assignees, $issue_id);
-+
-+        // save a history entry about this...
-+        History::add($issue_id, Auth::getUserID(), History::getTypeID('user_associated'),
-+                        "Issue assignment to changed (" . History::formatChanges(join(', ', $old_assignee_names), join(', ', $assignee_names)) . ") by " . User::getFullName(Auth::getUserID()));
++        return 1;
 +    }
++
+     /**
+      * Method used to delete all user assignments for a specific issue.
+@@ -1839,6 +1929,7 @@
+         if (is_array($issue_id)) {
+             $issue_id = implode(", ", $issue_id);
+         }
++        $deleted_order_list = self::getDeleteUserAssociationOrderList($issue_id);
+         $stmt = "DELETE FROM
+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
+                  WHERE
+@@ -1869,6 +1960,7 @@
+     {
+         $issue_id = Misc::escapeInteger($issue_id);
+         $usr_id = Misc::escapeInteger($usr_id);
++        $delete_order_list = self::getDeleteUserAssociationOrderList($issue_id, $usr_id);
+         $stmt = "DELETE FROM
+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
+                  WHERE
+@@ -2020,6 +2021,7 @@
+             History::add($issue_id, Auth::getUserID(), History::getTypeID('user_unassociated'),
+                 User::getFullName($usr_id) . ' removed from issue by ' . User::getFullName(Auth::getUserID()));
+         }
++        self::rearrangeDeleteUserAssociationOrderList($delete_order_list);
+         return 1;
+     }
+@@ -3253,6 +3352,8 @@
+         $ids = implode(", ", $ids);
+         $stmt = "SELECT
+                     isu_iss_id,
++                    isu_order,
++                    isu_usr_id,
+                     usr_full_name
+                  FROM
+                     " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
+@@ -3264,6 +3365,7 @@
+         if (PEAR::isError($res)) {
+             Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
+         } else {
++            // gather names of the users assigned to each issue
+             $t = array();
+             for ($i = 0; $i < count($res); $i++) {
+                 if (!empty($t[$res[$i]['isu_iss_id']])) {
+@@ -3272,9 +3374,18 @@
+                     $t[$res[$i]['isu_iss_id']] = $res[$i]['usr_full_name'];
+                 }
+             }
++            // gather orders
++            $o = array();
++            for ($i = 0; $i < count($res); $i++) {
++                if (empty($o[$res[$i]['isu_iss_id']])) {
++                    $o[$res[$i]['isu_iss_id']] = array();
++                }
++                $o[$res[$i]['isu_iss_id']][$res[$i]['isu_usr_id']] = $res[$i]['isu_order'];
++            }
+             // now populate the $result variable again
+             for ($i = 0; $i < count($result); $i++) {
+                 @$result[$i]['assigned_users'] = $t[$result[$i]['iss_id']];
++                @$result[$i]['assigned_users_order'] = $o[$result[$i]['iss_id']];
+             }
+         }
+     }
+@@ -4247,6 +4358,7 @@
+             Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
+             return -1;
+         }
++        self::moveOrderForAllUsers($issue_id, 1);
+     }
+@@ -4346,4 +4458,120 @@
+         History::add($issue_id, Auth::getUserID(), History::getTypeID('user_associated'),
+                         "Issue assignment to changed (" . History::formatChanges(join(', ', $old_assignee_names), join(', ', $assignee_names)) . ") by " . User::getFullName(Auth::getUserID()));
+     }
 +
 +    /**
 +     * Reorders user's issues as requested by user
 +        foreach ($res as $row) {
 +            self::reorderUserIssues($row["isu_usr_id"], $issue_id, $neworder);
 +        }
-+    }
-+    
-+}
---- eventum-2.2/templates/header.tpl.html      2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/templates/header.tpl.html        2009-10-12 22:10:36.435851675 +0300
-@@ -8,6 +8,7 @@
++    }
++    
+ }
+--- eventum-2.3.1/lib/eventum/class.search.php~        2011-04-20 17:22:15.000000000 +0300
++++ eventum-2.3.1/lib/eventum/class.search.php 2011-04-20 17:44:34.498519260 +0300
+@@ -63,6 +63,12 @@
+     {
+         $sort_by = self::getParam('sort_by');
+         $sort_order = self::getParam('sort_order');
++        $users = self::getParam('users');
++        if (empty($users) && $sort_by === 'isu_order') {
++            // Sorting by isu_order is impossible when no user specified
++            unset($sort_by);
++            unset($sort_order);
++        }
+         $rows = self::getParam('rows');
+         $hide_closed = self::getParam('hide_closed');
+         if ($hide_closed === '') {
+@@ -174,6 +174,7 @@
+             "iss_expected_resolution_date" => "desc",
+             "pre_title" => "asc",
+             "assigned" => "asc",
++            "isu_order" => "desc",
+         );
+         foreach ($custom_fields as $fld_id => $fld_name) {
+--- eventum-2.3.1/templates/header.tpl.html~   2011-09-15 09:36:55.000000000 +0300
++++ eventum-2.3.1/templates/header.tpl.html    2011-09-15 09:43:49.318473817 +0300
+@@ -18,6 +18,7 @@
  <script type="text/javascript" src="{$rel_url}js/jquery/form.js?c=9984"></script>
  <script type="text/javascript" src="{$rel_url}js/jquery/blockui.js?c=eb13"></script>
  <script type="text/javascript" src="{$rel_url}js/jquery/ui.datepicker.js?c=a911"></script>
 +<script type="text/javascript" src="{$rel_url}js/jquery/jquery.tablednd.js"></script>
- <link rel="stylesheet" href="{$rel_url}js/jquery/ui.datepicker.css?c=5096">
- <script type="text/javascript">
- <!--
+ <script type="text/javascript" src="{$rel_url}js/validation.js?c=ad33"></script>
+ <script type="text/javascript" src="{$rel_url}js/browserSniffer.js?c=c046"></script>
+ <script type="text/javascript" src="{$rel_url}js/global.js?c=50d6"></script>
 --- eventum-2.2/templates/list.tpl.html        2009-09-14 18:07:55.000000000 +0300
 +++ eventum-2.2-order/templates/list.tpl.html  2009-10-12 22:10:36.439185157 +0300
 @@ -92,6 +92,28 @@
            <td align="{$column.align|default:'center'}" class="default_white" nowrap {if $column.width != ''}width="{$column.width}"{/if}>
              {if $field_name == 'iss_summary'}
              <table cellspacing="0" cellpadding="1" width="100%">
-@@ -232,20 +261,21 @@
+@@ -268,8 +268,9 @@
            {/if}
            {/foreach}
          </tr>
            {if $current_role > $roles.developer}
            <td bgcolor="{$list[i].status_color}" width="1%" class="default" align="center"><input type="checkbox" name="item[]" value="{$list[i].iss_id}" onchange="toggleBulkUpdate();"></td>
            {/if}
-           {foreach from=$columns item=column key=field_name}
-           {if $field_name == 'custom_fields'}
-             {foreach from=$list[i].custom_field key=fld_id item=fld_value}
--                <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
--                  {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
-+                <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default custom_field" onclick="return updateCustomFields({$list[i].iss_id});">
-+                    {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
+@@ -280,8 +281,8 @@
+                   {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
                  </td>
                {/foreach}
 -          {else}
              {if $field_name == 'iss_id'}
                <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_id}</a>
              {elseif $field_name == 'pri_rank'}
-@@ -279,7 +309,7 @@
-             {elseif $field_name == 'iss_percent_complete'}
-               {$list[i].iss_percent_complete|escape:"html"}%
-             {elseif $field_name == 'iss_expected_resolution_date'}
--              {$list[i].iss_expected_resolution_date|escape:"html"}
-+              <div class="inline_date_pick" id="expected_resolution_date|{$list[i].iss_id}">{$list[i].iss_expected_resolution_date|escape:"html"}&nbsp;</div>
-             {elseif $field_name == 'iss_summary'}
-               <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_summary|escape:"html"}</a>
-               {if $list[i].redeemed}
 @@ -288,6 +318,8 @@
                {if $list[i].iss_private == 1}
                    <b>[Private]</b>
 +</script>
  {include file="app_info.tpl.html"}
  {include file="footer.tpl.html"}
---- eventum-2.2/templates/list.tpl.html.~1~    1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/templates/list.tpl.html.~1~      2009-09-14 18:07:55.000000000 +0300
-@@ -0,0 +1,360 @@
-+{include file="header.tpl.html" extra_title="List of Issues"}
-+{include file="navigation.tpl.html"}
-+
-+{if $current_role != $roles.customer}
-+{include file="quick_filter_form.tpl.html"}
-+{include file="current_filters.tpl.html"}
-+{/if}
-+<script type="text/javascript">
-+<!--
-+var page_url = '{$smarty.server.PHP_SELF}';
-+var current_page = {if $list_info.current_page == ''}0{else}{$list_info.current_page}{/if};
-+var last_page = {if $list_info.last_page == ''}0{else}{$list_info.last_page}{/if};
-+{literal}
-+// this function will display the bulk update form if any issues are selected.
-+// if no issues are selected it will be hidden.
-+function toggleBulkUpdate()
-+{
-+    var items = document.getElementsByName('item[]');
-+
-+    // loop through looking to see which are checked
-+    var show = false;
-+    for (var i = 0; i < items.length; i++) {
-+        if (items[i].checked) {
-+            show = true;
-+            break;
-+        }
-+    }
-+    if (show) {
-+        changeVisibility('bulk_update1', show);
-+    }
-+}
-+function resetBulkUpdate()
-+{
-+    var f = getForm('list_form');
-+    clearSelectedOptions(getFormElement(f, 'users[]'));
-+    clearSelectedOptions(getFormElement(f, 'status'));
-+    if (getFormElement(f, 'release')) {
-+        clearSelectedOptions(getFormElement(f, 'release'));
-+    }
-+}
-+function bulkUpdate()
-+{
-+    var f = getForm('list_form');
-+    if (!hasOneChecked(f, 'item[]')) {
-+        alert('{/literal}{t escape=js}Please choose which issues to update.{/t}{literal}');
-+        return false;
-+    }
-+
-+    // figure out what is changing
-+    var changed = new Array();
-+    if (hasOneSelected(f, 'users[]')) {
-+        changed[changed.length] = 'Assignment';
-+    }
-+    if (f.elements.status.selectedIndex != 0) {
-+        changed[changed.length] = 'Status';
-+    }
-+    if ((f.elements.release) && (f.elements.release.selectedIndex != 0)) {
-+        changed[changed.length] = 'Release';
-+    }
-+    if ((f.elements.priority) && (f.elements.priority.selectedIndex != 0)) {
-+        changed[changed.length] = 'Priority';
-+    }
-+    if ((f.elements.category) && (f.elements.category.selectedIndex != 0)) {
-+        changed[changed.length] = 'Category';
-+    }
-+    if ((f.elements.closed_status) && (f.elements.closed_status.selectedIndex != 0)) {
-+        changed[changed.length] = 'Closed Status';
-+    }
-+    if (changed.length < 1) {
-+        alert('{/literal}{t escape=js}Please choose new values for the selected issues{/t}{literal}');
-+        return false;
-+    }
-+    var msg = '{/literal}{t escape=js}Warning: If you continue, you will change the {/t}{literal}';
-+    for (var i = 0; i < changed.length; i++) {
-+        msg += changed[i];
-+        if ((changed.length > 1) && (i == (changed.length-2))) {
-+            msg += ' and ';
-+        } else {
-+            if (i != (changed.length-1)) {
-+                msg += ', ';
-+            }
-+        }
-+    }
-+    msg += ' {/literal}{t escape=js}for all selected issues. Are you sure you want to continue?{/t}{literal}';
-+    if (!confirm(msg)) {
-+        return false;
-+    }
-+    var features = 'width=420,height=200,top=30,left=30,resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
-+    var popupWin = window.open('', '_popup', features);
-+    popupWin.focus();
-+    f.action = 'popup.php';
-+    f.target = '_popup';
-+    f.submit();
-+}
-+function hideClosed(f)
-+{
-+    if (f.hide_closed.checked) {
-+        window.location.href = page_url + "?" + replaceParam(window.location.href, 'hide_closed', '1');
-+    } else {
-+        window.location.href = page_url + "?" + replaceParam(window.location.href, 'hide_closed', '0');
-+    }
-+}
-+function resizePager(f)
-+{
-+    var pagesize = f.page_size.options[f.page_size.selectedIndex].value;
-+    window.location.href = page_url + "?" + replaceParam(window.location.href, 'rows', pagesize);
-+}
-+function checkPageField(ev)
-+{
-+    // check if the user is trying to submit the form by hitting <enter>
-+    if (((window.event) && (window.event.keyCode == 13)) ||
-+            ((ev) && (ev.which == 13))) {
-+        return false;
-+    }
-+}
-+function goPage(f, new_page)
-+{
-+    if ((new_page > last_page+1) || (new_page <= 0) ||
-+            (new_page == current_page+1) || (!isNumberOnly(new_page))) {
-+        f.page.value = current_page+1;
-+        return false;
-+    }
-+    setPage(new_page-1);
-+}
-+function setPage(new_page)
-+{
-+    if ((new_page > last_page) || (new_page < 0) ||
-+            (new_page == current_page)) {
-+        return false;
-+    }
-+    window.location.href = page_url + "?" + replaceParam(window.location.href, 'pagerRow', new_page);
-+}
-+function downloadCSV()
-+{
-+    var f = this.document.csv_form;
-+    f.submit();
-+    return false;
-+}
-+window.onload = disableFields;
-+function disableFields()
-+{
-+    var f = document.list_form;
-+    if (current_page == 0) {
-+        f.first.disabled = true;
-+        f.previous.disabled = true;
-+    }
-+    if ((current_page == last_page) || (last_page <= 0)) {
-+        f.next.disabled = true;
-+        f.last.disabled = true;
-+    }
-+    if ((current_page == 0) && (last_page <= 0)) {
-+        f.page.disabled = true;
-+        f.go.disabled = true;
-+    }
-+}
-+//-->
-+</script>
-+{/literal}
-+{* Figure out how many columns there are *}
-+{assign var="col_count" value=$columns|@count}
-+{if $list_info.custom_fields|@count > 1}
-+{math assign="col_count" equation="x+y-1" x=$col_count y=$list_info.custom_fields|@count}
-+{/if}
-+{if $current_role > $roles.developer}
-+    {math assign="col_count" equation="x+1" x=$col_count}
-+{/if}
-+<table width="100%" bgcolor="{$cell_color}" border="0" cellspacing="0" cellpadding="1" align="center">
-+  <form name="list_form" method="post" action="{$smarty.server.PHP_SELF}">
-+  <input type="hidden" name="cat" value="bulk_update">
-+  <tr>
-+    <td>
-+      <table bgcolor="#FFFFFF" width="100%" cellspacing="1" cellpadding="2" border="0">
-+        <tr>
-+          <td colspan="{$col_count}" class="default">
-+            <table width="100%" cellspacing="0" cellpadding="0" border="0">
-+              <tr>
-+                <td class="default">
-+                  <b>{t}Search Results{/t} ({$list_info.total_rows} {t}issues found{/t}{if $list_info.end_offset > 0}, {math equation="x + 1" x=$list_info.start_offset} - {$list_info.end_offset} {t}shown{/t}{/if})</b>
-+                  {include file="help_link.tpl.html" topic="list"}
-+                </td>
-+                {if $current_role != $roles.customer}
-+                <td align="right" class="default" nowrap>
-+                  {if $browser.ie5up or $browser.ns6up or $browser.gecko or $browser.safari or $browser.opera5up}
-+                  {t}hide/show{/t}:
-+                  [ <a class="link" title="{t}hide / show the quick search form{/t}" href="javascript:void(null);" onClick="javascript:toggleVisibility('filter_form');">{t}quick search{/t}</a> ]
-+                  {if $current_role > $roles.reporter}[ <a class="link" title="{t}hide / show the advanced search form{/t}" href="javascript:void(null);" onClick="javascript:toggleVisibility('custom_filter_form');">{t}advanced search{/t}</a> ]{/if}
-+                  {if $current_role > $roles.reporter}[ <a class="link" href="javascript:void(null);" onClick="javascript:toggleVisibility('current_filters');"> {t}current filters{/t}</a> ]{/if}
-+                  {if $current_role > $roles.developer}[ <a class="link" href="javascript:void(null);" onClick="javascript:toggleVisibility('bulk_update');"> {t}bulk update tool{/t}</a> ]{/if}
-+                  {/if}
-+                </td>
-+                {/if}
-+              </tr>
-+            </table>
-+          </td>
-+        </tr>
-+        <tr bgcolor="{$cell_color}">
-+          {if $current_role > $roles.developer}
-+          <td width="1%">
-+            <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');toggleBulkUpdate();">
-+          </td>
-+          {/if}
-+          {foreach from=$columns item=column key=field_name}
-+          {if $field_name == 'custom_fields'}
-+            {foreach from=$list_info.custom_fields key=fld_id item=fld_title}
-+              {assign var="fld_name_id" value="custom_field_`$fld_id`"}
-+                <td align="{$column.align|default:'center'}" class="default_white" nowrap>
-+                  <a title="{t}sort by{/t} {$fld_title|escape:"html"}" href="{$sorting.links[$fld_name_id]}" class="white_link">{$fld_title|escape:"html"}</a>
-+                  {if $sorting.images[$fld_name_id] != ""}<a title="{t}sort by{/t} {$fld_title|escape:"html"}" href="{$sorting.links[$fld_name_id]}" class="white_link"><img border="0" src="{$sorting.images[$fld_name_id]}"></a>{/if}
-+                </td>
-+              {/foreach}
-+          {else}
-+          <td align="{$column.align|default:'center'}" class="default_white" nowrap {if $column.width != ''}width="{$column.width}"{/if}>
-+            {if $field_name == 'iss_summary'}
-+            <table cellspacing="0" cellpadding="1" width="100%">
-+              <tr>
-+                <td class="default_white">
-+                  <a title="{t}sort by summary{/t}" href="{$sorting.links.iss_summary}" class="white_link">{t}Summary{/t}</a>
-+                  {if $sorting.images.iss_summary != ""}<a title="{t}sort by summary{/t}" href="{$sorting.links.iss_summary}" class="white_link"><img border="0" src="{$sorting.images.iss_summary}"></a>{/if}
-+                </td>
-+                <td align="right">
-+                  <span class="default_white">{t}Export Data:{/t}</span> <input alt="{t}generate excel-friendly report{/t}" type="image" src="{$rel_url}images/excel.jpg" class="shortcut" value="{t}Export to Excel{/t}" onClick="javascript:return downloadCSV();">
-+                </td>
-+              </tr>
-+            </table>
-+            {elseif $sorting.links[$field_name] != ''}
-+              <a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link">{$column.title}</a>
-+              {if $sorting.images[$field_name] != ""}<a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link"><img border="0" src="{$sorting.images[$field_name]}"></a>{/if}
-+            {else}
-+              {$column.title}
-+            {/if}
-+          </td>
-+          {/if}
-+          {/foreach}
-+        </tr>
-+        {section name="i" loop=$list}
-+        <tr {if $current_role >= $roles.developer AND $list[i].iqu_status > 0}style="text-decoration: line-through;"{/if}>
-+          {if $current_role > $roles.developer}
-+          <td bgcolor="{$list[i].status_color}" width="1%" class="default" align="center"><input type="checkbox" name="item[]" value="{$list[i].iss_id}" onchange="toggleBulkUpdate();"></td>
-+          {/if}
-+          {foreach from=$columns item=column key=field_name}
-+          {if $field_name == 'custom_fields'}
-+            {foreach from=$list[i].custom_field key=fld_id item=fld_value}
-+                <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
-+                  {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
-+                </td>
-+              {/foreach}
-+          {else}
-+          <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
-+            {if $field_name == 'iss_id'}
-+              <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_id}</a>
-+            {elseif $field_name == 'pri_rank'}
-+              {$list[i].pri_title|escape:"html"}
-+            {elseif $field_name == 'iss_grp_id'}
-+              {$list[i].group}
-+            {elseif $field_name == 'assigned'}
-+              {$list[i].assigned_users}
-+            {elseif $field_name == 'time_spent'}
-+              {$list[i].time_spent}
-+            {elseif $field_name == 'prc_title'}
-+              {$list[i].prc_title}
-+            {elseif $field_name == 'pre_title'}
-+              {$list[i].pre_title|escape:"html"}
-+            {elseif $field_name == 'iss_customer_id'}
-+              {$list[i].customer_title|escape:"html"}
-+            {elseif $field_name == 'support_level'}
-+              {$list[i].support_level|escape:"html"}
-+            {elseif $field_name == 'sta_rank'}
-+              {$list[i].sta_title|escape:"html"}
-+            {elseif $field_name == 'iss_created_date'}
-+              {$list[i].iss_created_date|escape:"html"}
-+            {elseif $field_name == 'iss_dev_time'}
-+              {$list[i].iss_dev_time|escape:"html"}
-+            {elseif $field_name == 'sta_change_date'}
-+              {$list[i].status_change_date|escape:"html"}
-+            {elseif $field_name == 'last_action_date'}
-+              {$list[i].last_action_date|escape:"html"}
-+            {elseif $field_name == 'usr_full_name'}
-+              {$list[i].usr_full_name|escape:"html"}
-+            {elseif $field_name == 'iss_percent_complete'}
-+              {$list[i].iss_percent_complete|escape:"html"}%
-+            {elseif $field_name == 'iss_expected_resolution_date'}
-+              {$list[i].iss_expected_resolution_date|escape:"html"}
-+            {elseif $field_name == 'iss_summary'}
-+              <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_summary|escape:"html"}</a>
-+              {if $list[i].redeemed}
-+                  [Redeemed]
-+              {/if}
-+              {if $list[i].iss_private == 1}
-+                  <b>[Private]</b>
-+              {/if}
-+            {/if}
-+          </td>
-+          {/if}
-+          {/foreach}
-+        </tr>
-+        {sectionelse}
-+        <tr bgcolor="gray">
-+          <td colspan="{$col_count}" class="default_white" align="center">
-+            <i>{t}No issues could be found.{/t}</i>
-+          </td>
-+        </tr>
-+        {/section}
-+        <tr bgcolor="{$cell_color}">
-+          <td colspan="{$col_count}">
-+            <table width="100%" cellspacing="0" cellpadding="0">
-+              <tr>
-+                <td width="30%" nowrap>
-+                  {if $current_role > $roles.developer}
-+                  <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');">
-+                  {/if}
-+                </td>
-+                <td width="40%" align="center" nowrap>
-+                  <nobr>
-+                  <input name="first" type="button" value="|&lt;" class="shortcut" onClick="javascript:setPage(0);">
-+                  <input name="previous" type="button" value="&lt;&lt;" class="shortcut" onClick="javascript:setPage({$list_info.previous_page});">
-+                  <input type="text" name="page" size="3" maxlength="3" value="{if $list_info.current_page == ''}1{else}{math equation="x + 1" x=$list_info.current_page}{/if}" style="background: {$cell_color};" class="paging_input" onKeyPress="javascript:return checkPageField(event);">
-+                  <input name="go" type="button" value="{t}Go{/t}" class="shortcut" onClick="javascript:goPage(this.form, this.form.page.value);">
-+                  <input name="next" type="button" value="&gt;&gt;" class="shortcut" onClick="javascript:setPage({$list_info.next_page});">
-+                  <input name="last" type="button" value="&gt;|" class="shortcut" onClick="javascript:setPage({$list_info.last_page});">
-+                  </nobr>
-+                </td>
-+                <td nowrap>
-+                  <span class="default_white">{t}Rows per Page:{/t}</span>
-+                  <select name="page_size" class="default" onChange="javascript:resizePager(this.form);">
-+                    <option value="5" {if $options.rows == 5}selected{/if}>5</option>
-+                    <option value="10" {if $options.rows == 10}selected{/if}>10</option>
-+                    <option value="25" {if $options.rows == 25}selected{/if}>25</option>
-+                    <option value="50" {if $options.rows == 50}selected{/if}>50</option>
-+                    <option value="100" {if $options.rows == 100}selected{/if}>100</option>
-+                    <option value="ALL" {if $options.rows == "ALL"}selected{/if}>{t}ALL{/t}</option>
-+                  </select>
-+                  <input type="button" value="{t}Set{/t}" class="shortcut" onClick="javascript:resizePager(this.form);">
-+                </td>
-+                <td width="30%" class="default_white" align="right">
-+                  <input type="checkbox" id="hide_closed" name="hide_closed" {if $options.hide_closed}checked{/if} onClick="javascript:hideClosed(this.form);"> <label for="hide_closed">{t}Hide Closed Issues{/t}</label>&nbsp;
-+                </td>
-+              </tr>
-+            </table>
-+          </td>
-+        </tr>
-+      </table>
-+    </td>
-+  </tr>
-+  {if $current_role > $roles.developer}
-+  <tr>
-+    <td bgcolor="#FFFFFF">
-+      <br />
-+      {include file="bulk_update.tpl.html"}
-+    </td>
-+  </tr>
-+  {/if}
-+  </form>
-+  <form target="_csvWindow" method="post" action="csv.php" name="csv_form">
-+  <input type="hidden" name="csv_data" value="{$csv_data}">
-+  </form>
-+</table>
-+<br />
-+
-+{include file="app_info.tpl.html"}
-+{include file="footer.tpl.html"}
---- eventum-2.2/templates/list.tpl.html.~2~    1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/templates/list.tpl.html.~2~      2009-10-12 22:10:36.435851675 +0300
-@@ -0,0 +1,421 @@
-+{include file="header.tpl.html" extra_title="List of Issues"}
-+{include file="navigation.tpl.html"}
-+
-+{if $current_role != $roles.customer}
-+{include file="quick_filter_form.tpl.html"}
-+{include file="current_filters.tpl.html"}
-+{/if}
-+<script type="text/javascript">
-+<!--
-+var page_url = '{$smarty.server.PHP_SELF}';
-+var current_page = {if $list_info.current_page == ''}0{else}{$list_info.current_page}{/if};
-+var last_page = {if $list_info.last_page == ''}0{else}{$list_info.last_page}{/if};
-+{literal}
-+// this function will display the bulk update form if any issues are selected.
-+// if no issues are selected it will be hidden.
-+function toggleBulkUpdate()
-+{
-+    var items = document.getElementsByName('item[]');
-+
-+    // loop through looking to see which are checked
-+    var show = false;
-+    for (var i = 0; i < items.length; i++) {
-+        if (items[i].checked) {
-+            show = true;
-+            break;
-+        }
-+    }
-+    if (show) {
-+        changeVisibility('bulk_update1', show);
-+    }
-+}
-+function resetBulkUpdate()
-+{
-+    var f = getForm('list_form');
-+    clearSelectedOptions(getFormElement(f, 'users[]'));
-+    clearSelectedOptions(getFormElement(f, 'status'));
-+    if (getFormElement(f, 'release')) {
-+        clearSelectedOptions(getFormElement(f, 'release'));
-+    }
-+}
-+function bulkUpdate()
-+{
-+    var f = getForm('list_form');
-+    if (!hasOneChecked(f, 'item[]')) {
-+        alert('{/literal}{t escape=js}Please choose which issues to update.{/t}{literal}');
-+        return false;
-+    }
-+
-+    // figure out what is changing
-+    var changed = new Array();
-+    if (hasOneSelected(f, 'users[]')) {
-+        changed[changed.length] = 'Assignment';
-+    }
-+    if (f.elements.status.selectedIndex != 0) {
-+        changed[changed.length] = 'Status';
-+    }
-+    if ((f.elements.release) && (f.elements.release.selectedIndex != 0)) {
-+        changed[changed.length] = 'Release';
-+    }
-+    if ((f.elements.priority) && (f.elements.priority.selectedIndex != 0)) {
-+        changed[changed.length] = 'Priority';
-+    }
-+    if ((f.elements.category) && (f.elements.category.selectedIndex != 0)) {
-+        changed[changed.length] = 'Category';
-+    }
-+    if ((f.elements.closed_status) && (f.elements.closed_status.selectedIndex != 0)) {
-+        changed[changed.length] = 'Closed Status';
-+    }
-+    if (changed.length < 1) {
-+        alert('{/literal}{t escape=js}Please choose new values for the selected issues{/t}{literal}');
-+        return false;
-+    }
-+    var msg = '{/literal}{t escape=js}Warning: If you continue, you will change the {/t}{literal}';
-+    for (var i = 0; i < changed.length; i++) {
-+        msg += changed[i];
-+        if ((changed.length > 1) && (i == (changed.length-2))) {
-+            msg += ' and ';
-+        } else {
-+            if (i != (changed.length-1)) {
-+                msg += ', ';
-+            }
-+        }
-+    }
-+    msg += ' {/literal}{t escape=js}for all selected issues. Are you sure you want to continue?{/t}{literal}';
-+    if (!confirm(msg)) {
-+        return false;
-+    }
-+    var features = 'width=420,height=200,top=30,left=30,resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
-+    var popupWin = window.open('', '_popup', features);
-+    popupWin.focus();
-+    f.action = 'popup.php';
-+    f.target = '_popup';
-+    f.submit();
-+}
-+function reorderBulk(order_user, neworder)
-+{
-+    url = page_url + "?";
-+    url += "reorder_user=" + order_user;
-+
-+    items = document.getElementsByName("item[]");
-+    checkedcount = 0;
-+    for (var i = 0; i < items.length; i++) {
-+      if (items[i].checked) {
-+        url += "&reorder_source[" + checkedcount + "]=" + items[i].value;
-+        checkedcount++;
-+      }
-+    }
-+    if (checkedcount == 0) {
-+        alert('{/literal}{t escape=js}Please choose which issues to move to the new place.{/t}{literal}');
-+        return false;
-+    }
-+
-+    url += "&reorder_neworder=" + neworder;
-+    
-+    window.location.href = url;
-+}
-+function hideClosed(f)
-+{
-+    if (f.hide_closed.checked) {
-+        window.location.href = page_url + "?" + replaceParam(window.location.href, 'hide_closed', '1');
-+    } else {
-+        window.location.href = page_url + "?" + replaceParam(window.location.href, 'hide_closed', '0');
-+    }
-+}
-+function resizePager(f)
-+{
-+    var pagesize = f.page_size.options[f.page_size.selectedIndex].value;
-+    window.location.href = page_url + "?" + replaceParam(window.location.href, 'rows', pagesize);
-+}
-+function checkPageField(ev)
-+{
-+    // check if the user is trying to submit the form by hitting <enter>
-+    if (((window.event) && (window.event.keyCode == 13)) ||
-+            ((ev) && (ev.which == 13))) {
-+        return false;
-+    }
-+}
-+function goPage(f, new_page)
-+{
-+    if ((new_page > last_page+1) || (new_page <= 0) ||
-+            (new_page == current_page+1) || (!isNumberOnly(new_page))) {
-+        f.page.value = current_page+1;
-+        return false;
-+    }
-+    setPage(new_page-1);
-+}
-+function setPage(new_page)
-+{
-+    if ((new_page > last_page) || (new_page < 0) ||
-+            (new_page == current_page)) {
-+        return false;
-+    }
-+    window.location.href = page_url + "?" + replaceParam(window.location.href, 'pagerRow', new_page);
-+}
-+function downloadCSV()
-+{
-+    var f = this.document.csv_form;
-+    f.submit();
-+    return false;
-+}
-+window.onload = disableFields;
-+function disableFields()
-+{
-+    var f = document.list_form;
-+    if (current_page == 0) {
-+        f.first.disabled = true;
-+        f.previous.disabled = true;
-+    }
-+    if ((current_page == last_page) || (last_page <= 0)) {
-+        f.next.disabled = true;
-+        f.last.disabled = true;
-+    }
-+    if ((current_page == 0) && (last_page <= 0)) {
-+        f.page.disabled = true;
-+        f.go.disabled = true;
-+    }
-+}
-+function updateCustomFields(issue_id)
-+{
-+    var features = 'width=560,height=460,top=30,left=30,resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
-+    var customWin = window.open('custom_fields.php?issue_id=' + issue_id, '_custom_fields', features);
-+    customWin.focus();
-+    return false;
-+}
-+//-->
-+</script>
-+{/literal}
-+{* Figure out how many columns there are *}
-+{assign var="col_count" value=$columns|@count}
-+{if $list_info.custom_fields|@count > 1}
-+{math assign="col_count" equation="x+y-1" x=$col_count y=$list_info.custom_fields|@count}
-+{/if}
-+{if $current_role > $roles.developer}
-+    {math assign="col_count" equation="x+1" x=$col_count}
-+{/if}
-+<table width="100%" bgcolor="{$cell_color}" border="0" cellspacing="0" cellpadding="1" align="center">
-+  <form name="list_form" method="post" action="{$smarty.server.PHP_SELF}">
-+  <input type="hidden" name="cat" value="bulk_update">
-+  <tr>
-+    <td>
-+      <table bgcolor="#FFFFFF" width="100%" cellspacing="1" cellpadding="2" border="0" id="issue_list_table">
-+        <tr class="nodrag">
-+          <td colspan="{$col_count}" class="default">
-+            <table width="100%" cellspacing="0" cellpadding="0" border="0">
-+              <tr class="nodrag">
-+                <td class="default">
-+                  <b>{t}Search Results{/t} ({$list_info.total_rows} {t}issues found{/t}{if $list_info.end_offset > 0}, {math equation="x + 1" x=$list_info.start_offset} - {$list_info.end_offset} {t}shown{/t}{/if})</b>
-+                  {include file="help_link.tpl.html" topic="list"}
-+                </td>
-+                {if $current_role != $roles.customer}
-+                <td align="right" class="default" nowrap>
-+                  {if $browser.ie5up or $browser.ns6up or $browser.gecko or $browser.safari or $browser.opera5up}
-+                  {t}hide/show{/t}:
-+                  [ <a class="link" title="{t}hide / show the quick search form{/t}" href="javascript:void(null);" onClick="javascript:toggleVisibility('filter_form');">{t}quick search{/t}</a> ]
-+                  {if $current_role > $roles.reporter}[ <a class="link" title="{t}hide / show the advanced search form{/t}" href="javascript:void(null);" onClick="javascript:toggleVisibility('custom_filter_form');">{t}advanced search{/t}</a> ]{/if}
-+                  {if $current_role > $roles.reporter}[ <a class="link" href="javascript:void(null);" onClick="javascript:toggleVisibility('current_filters');"> {t}current filters{/t}</a> ]{/if}
-+                  {if $current_role > $roles.developer}[ <a class="link" href="javascript:void(null);" onClick="javascript:toggleVisibility('bulk_update');"> {t}bulk update tool{/t}</a> ]{/if}
-+                  {/if}
-+                </td>
-+                {/if}
-+              </tr>
-+            </table>
-+          </td>
-+        </tr>
-+        <tr bgcolor="{$cell_color}" class="nodrag">
-+          {if $current_role > $roles.developer}
-+          <td width="1%">
-+            <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');toggleBulkUpdate();">
-+          </td>
-+          {/if}
-+          {foreach from=$columns item=column key=field_name}
-+          {if $field_name == 'custom_fields'}
-+            {foreach from=$list_info.custom_fields key=fld_id item=fld_title}
-+              {assign var="fld_name_id" value="custom_field_`$fld_id`"}
-+                <td align="{$column.align|default:'center'}" class="default_white" nowrap>
-+                  <a title="{t}sort by{/t} {$fld_title|escape:"html"}" href="{$sorting.links[$fld_name_id]}" class="white_link">{$fld_title|escape:"html"}</a>
-+                  {if $sorting.images[$fld_name_id] != ""}<a title="{t}sort by{/t} {$fld_title|escape:"html"}" href="{$sorting.links[$fld_name_id]}" class="white_link"><img border="0" src="{$sorting.images[$fld_name_id]}"></a>{/if}
-+                </td>
-+              {/foreach}
-+          {elseif $field_name != 'isu_order' || $isu_order_user}
-+          <td align="{$column.align|default:'center'}" class="default_white" nowrap {if $column.width != ''}width="{$column.width}"{/if}>
-+            {if $field_name == 'iss_summary'}
-+            <table cellspacing="0" cellpadding="1" width="100%">
-+              <tr>
-+                <td class="default_white">
-+                  <a title="{t}sort by summary{/t}" href="{$sorting.links.iss_summary}" class="white_link">{t}Summary{/t}</a>
-+                  {if $sorting.images.iss_summary != ""}<a title="{t}sort by summary{/t}" href="{$sorting.links.iss_summary}" class="white_link"><img border="0" src="{$sorting.images.iss_summary}"></a>{/if}
-+                </td>
-+                <td align="right">
-+                  <span class="default_white">{t}Export Data:{/t}</span> <input alt="{t}generate excel-friendly report{/t}" type="image" src="{$rel_url}images/excel.jpg" class="shortcut" value="{t}Export to Excel{/t}" onClick="javascript:return downloadCSV();">
-+                </td>
-+              </tr>
-+            </table>
-+            {elseif $sorting.links[$field_name] != ''}
-+              <a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link">{$column.title}</a>
-+              {if $field_name == 'isu_order'}
-+                 <br>{$users[$isu_order_user]}
-+              {/if}
-+              {if $sorting.images[$field_name] != ""}<a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link"><img border="0" src="{$sorting.images[$field_name]}"></a>{/if}
-+            {else}
-+              {$column.title}
-+            {/if}
-+          </td>
-+          {/if}
-+          {/foreach}
-+        </tr>
-+        <tbody>
-+        {section name="i" loop=$list}
-+        <tr {if $current_role >= $roles.developer AND $list[i].iqu_status > 0}style="text-decoration: line-through;"{/if} id="{$list[i].iss_id}" {if !$list[i].assigned_users_order[$current_user_id]}class="nodrag"{/if}>
-+          {if $current_role > $roles.developer}
-+          <td bgcolor="{$list[i].status_color}" width="1%" class="default" align="center"><input type="checkbox" name="item[]" value="{$list[i].iss_id}" onchange="toggleBulkUpdate();"></td>
-+          {/if}
-+          {foreach from=$columns item=column key=field_name}
-+          {if $field_name == 'custom_fields'}
-+            {foreach from=$list[i].custom_field key=fld_id item=fld_value}
-+                <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default custom_field" onclick="return updateCustomFields({$list[i].iss_id});">
-+                    {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
-+                </td>
-+              {/foreach}
-+          {elseif $field_name != 'isu_order' || $isu_order_user}
-+          <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
-+            {if $field_name == 'iss_id'}
-+              <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_id}</a>
-+            {elseif $field_name == 'pri_rank'}
-+              {$list[i].pri_title|escape:"html"}
-+            {elseif $field_name == 'iss_grp_id'}
-+              {$list[i].group}
-+            {elseif $field_name == 'assigned'}
-+              {$list[i].assigned_users}
-+            {elseif $field_name == 'time_spent'}
-+              {$list[i].time_spent}
-+            {elseif $field_name == 'prc_title'}
-+              {$list[i].prc_title}
-+            {elseif $field_name == 'pre_title'}
-+              {$list[i].pre_title|escape:"html"}
-+            {elseif $field_name == 'iss_customer_id'}
-+              {$list[i].customer_title|escape:"html"}
-+            {elseif $field_name == 'support_level'}
-+              {$list[i].support_level|escape:"html"}
-+            {elseif $field_name == 'sta_rank'}
-+              {$list[i].sta_title|escape:"html"}
-+            {elseif $field_name == 'iss_created_date'}
-+              {$list[i].iss_created_date|escape:"html"}
-+            {elseif $field_name == 'iss_dev_time'}
-+              {$list[i].iss_dev_time|escape:"html"}
-+            {elseif $field_name == 'sta_change_date'}
-+              {$list[i].status_change_date|escape:"html"}
-+            {elseif $field_name == 'last_action_date'}
-+              {$list[i].last_action_date|escape:"html"}
-+            {elseif $field_name == 'usr_full_name'}
-+              {$list[i].usr_full_name|escape:"html"}
-+            {elseif $field_name == 'iss_percent_complete'}
-+              {$list[i].iss_percent_complete|escape:"html"}%
-+            {elseif $field_name == 'iss_expected_resolution_date'}
-+              <div class="inline_date_pick" id="expected_resolution_date|{$list[i].iss_id}">{$list[i].iss_expected_resolution_date|escape:"html"}&nbsp;</div>
-+            {elseif $field_name == 'iss_summary'}
-+              <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_summary|escape:"html"}</a>
-+              {if $list[i].redeemed}
-+                  [Redeemed]
-+              {/if}
-+              {if $list[i].iss_private == 1}
-+                  <b>[Private]</b>
-+              {/if}
-+            {elseif $field_name == 'isu_order'}
-+              {if $list[i].assigned_users_order[$current_user_id]}
-+                <img src="{$rel_url}images/updown.gif" alt="move">
-+              {/if}
-+            {/if}
-+          </td>
-+          {/if}
-+          {/foreach}
-+        </tr>
-+        {sectionelse}
-+        <tr bgcolor="gray">
-+          <td colspan="{$col_count}" class="default_white" align="center">
-+            <i>{t}No issues could be found.{/t}</i>
-+          </td>
-+        </tr>
-+        {/section}
-+      </tbody>
-+        <tr bgcolor="{$cell_color}" class="nodrag">
-+          <td colspan="{$col_count}">
-+            <table width="100%" cellspacing="0" cellpadding="0">
-+              <tr class="nodrag">
-+                <td width="30%" nowrap>
-+                  {if $current_role > $roles.developer}
-+                  <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');">
-+                  {/if}
-+                </td>
-+                <td width="40%" align="center" nowrap>
-+                  <nobr>
-+                  <input name="first" type="button" value="|&lt;" class="shortcut" onClick="javascript:setPage(0);">
-+                  <input name="previous" type="button" value="&lt;&lt;" class="shortcut" onClick="javascript:setPage({$list_info.previous_page});">
-+                  <input type="text" name="page" size="3" maxlength="3" value="{if $list_info.current_page == ''}1{else}{math equation="x + 1" x=$list_info.current_page}{/if}" style="background: {$cell_color};" class="paging_input" onKeyPress="javascript:return checkPageField(event);">
-+                  <input name="go" type="button" value="{t}Go{/t}" class="shortcut" onClick="javascript:goPage(this.form, this.form.page.value);">
-+                  <input name="next" type="button" value="&gt;&gt;" class="shortcut" onClick="javascript:setPage({$list_info.next_page});">
-+                  <input name="last" type="button" value="&gt;|" class="shortcut" onClick="javascript:setPage({$list_info.last_page});">
-+                  </nobr>
-+                </td>
-+                <td nowrap>
-+                  <span class="default_white">{t}Rows per Page:{/t}</span>
-+                  <select name="page_size" class="default" onChange="javascript:resizePager(this.form);">
-+                    <option value="5" {if $options.rows == 5}selected{/if}>5</option>
-+                    <option value="10" {if $options.rows == 10}selected{/if}>10</option>
-+                    <option value="25" {if $options.rows == 25}selected{/if}>25</option>
-+                    <option value="50" {if $options.rows == 50}selected{/if}>50</option>
-+                    <option value="100" {if $options.rows == 100}selected{/if}>100</option>
-+                    <option value="ALL" {if $options.rows == "ALL"}selected{/if}>{t}ALL{/t}</option>
-+                  </select>
-+                  <input type="button" value="{t}Set{/t}" class="shortcut" onClick="javascript:resizePager(this.form);">
-+                </td>
-+                <td width="30%" class="default_white" align="right">
-+                  <input type="checkbox" id="hide_closed" name="hide_closed" {if $options.hide_closed}checked{/if} onClick="javascript:hideClosed(this.form);"> <label for="hide_closed">{t}Hide Closed Issues{/t}</label>&nbsp;
-+                </td>
-+              </tr>
-+            </table>
-+          </td>
-+        </tr>
-+      </table>
-+    </td>
-+  </tr>
-+  {if $current_role > $roles.developer}
-+  <tr>
-+    <td bgcolor="#FFFFFF">
-+      <br />
-+      {include file="bulk_update.tpl.html"}
-+    </td>
-+  </tr>
-+  {/if}
-+  </form>
-+  <form target="_csvWindow" method="post" action="csv.php" name="csv_form">
-+  <input type="hidden" name="csv_data" value="{$csv_data}">
-+  </form>
-+</table>
-+<br />
-+<script type="text/javascript">
-+{*
-+ * Order issues by drag and drop:
-+ * only if sorted by order and viewing your own issues
-+ *}
-+{if $options.sort_by == "isu_order" and $current_user_id == $isu_order_user}
-+{literal}
-+var before = ''; // make it global variable
-+$('#issue_list_table').tableDnD({
-+    onDragClass: "tDnD_whileDrag",
-+    onDragStart: function(table, row) {
-+        before = $.tableDnD.serialize('id');
-+    },
-+    onDrop: function(table, row) {
-+        $.post("/ajax/order.php", {before: before, after: $.tableDnD.serialize('id')}, function(data) {
-+                if (data.length > 0) {
-+                alert(data);
-+            }
-+        }, "text");
-+    }
-+});
-+{/literal}
-+{/if}
-+</script>
-+{include file="app_info.tpl.html"}
-+{include file="footer.tpl.html"}
-\ No newline at end of file
---- eventum-2.2/upgrade/patches/04_isu_order.php       1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/upgrade/patches/04_isu_order.php 2009-10-12 22:10:36.439185157 +0300
-@@ -0,0 +1,15 @@
-+<?php
-+
-+function db_patch_4() {
-+      $stmts = array();
-+
-+      $columns = db_getCol('DESC %TABLE_PREFIX%issue_user');
-+      if (in_array('isu_order', $columns)) {
-+              return $stmts;
-+      }
-+
-+      $stmts[] = "ALTER TABLE %TABLE_PREFIX%issue_user ADD isu_order int(11) NOT NULL DEFAULT '0' AFTER isu_assigned_date, ADD INDEX isu_order (isu_order)";
-+      $stmts[] = "UPDATE %TABLE_PREFIX%issue_user set isu_order=isu_iss_id";
-+
-+      return $stmts;
-+}
---- eventum-2.2/upgrade/update-database.php    2009-09-14 18:07:55.000000000 +0300
-+++ eventum-2.2-order/upgrade/update-database.php      2009-10-12 22:10:36.439185157 +0300
-@@ -73,6 +73,7 @@
-               1 => '01_notes.php',
-               2 => '02_usr_alias.php',
-               3 => '03_prj_mail_aliases.php',
-+              4 => '04_isu_order.php',
-       );
-       // sanity check. check that the version table exists.
---- eventum-2.2/upgrade/update-database.php.~1~        1970-01-01 02:00:00.000000000 +0200
-+++ eventum-2.2-order/upgrade/update-database.php.~1~  2009-09-14 18:07:55.000000000 +0300
-@@ -0,0 +1,133 @@
-+#!/usr/bin/php
-+<?php
-+require_once 'init.php';
-+
-+// on fresh install config is empty or missing
-+if (!defined('APP_SQL_DBNAME')) {
-+      fwrite(STDERR, "Eventum not configured. Please run setup.\n");
-+      exit(1);
-+}
-+
-+define('EXIT_OK', 0);
-+define('EXIT_ERROR', 1);
-+
-+function db_getAll($query) {
-+      $query = str_replace('%TABLE_PREFIX%', APP_TABLE_PREFIX, $query);
-+      $query = str_replace('%DBNAME%', APP_SQL_DBNAME, $query);
-+
-+      $res = DB_Helper::getInstance()->getAll($query, DB_FETCHMODE_ASSOC);
-+      if (PEAR::isError($res)) {
-+              echo $res->getMessage(), ': ', $res->getDebugInfo(), "\n";
-+              exit(1);
-+      }
-+      return $res;
-+}
-+
-+function db_getOne($query) {
-+      $query = str_replace('%TABLE_PREFIX%', APP_TABLE_PREFIX, $query);
-+      $query = str_replace('%DBNAME%', APP_SQL_DBNAME, $query);
-+
-+      $res = DB_Helper::getInstance()->getOne($query);
-+      if (PEAR::isError($res)) {
-+              echo $res->getMessage(), ': ', $res->getDebugInfo(), "\n";
-+              exit(1);
-+      }
-+      return $res;
-+}
-+
-+function db_getCol($query) {
-+      $query = str_replace('%TABLE_PREFIX%', APP_TABLE_PREFIX, $query);
-+      $query = str_replace('%DBNAME%', APP_SQL_DBNAME, $query);
-+
-+      $res = DB_Helper::getInstance()->getCol($query);
-+      if (PEAR::isError($res)) {
-+              echo $res->getMessage(), ': ', $res->getDebugInfo(), "\n";
-+              exit(1);
-+      }
-+      return $res;
-+}
-+
-+function db_query($query) {
-+      $query = str_replace('%TABLE_PREFIX%', APP_TABLE_PREFIX, $query);
-+      $query = str_replace('%DBNAME%', APP_SQL_DBNAME, $query);
-+
-+      $res = DB_Helper::getInstance()->query($query);
-+      if (PEAR::isError($res)) {
-+              echo $res->getMessage(), ': ', $res->getDebugInfo(), "\n";
-+              exit(1);
-+      }
-+      return $res;
-+}
-+
-+function apply_db_changes($stmts) {
-+      foreach ($stmts as $stmt) {
-+              db_query($stmt);
-+      }
-+}
-+
-+function patch_database() {
-+      /*
-+       * database versions. each version script can create it's dynamic queries
-+       */
-+      $versions = array(
-+              1 => '01_notes.php',
-+              2 => '02_usr_alias.php',
-+              3 => '03_prj_mail_aliases.php',
-+      );
-+
-+      // sanity check. check that the version table exists.
-+      $version = db_getOne("SELECT ver_version FROM %TABLE_PREFIX%version");
-+      if (!isset($version)) {
-+              # insert initial value
-+              db_query("INSERT INTO %TABLE_PREFIX%version SET ver_version=0");
-+              $version = 0;
-+      }
-+      $target = max(array_keys($versions));
-+      echo "Current database version: $version; Versions available: $target\n";
-+      if ($target < $version) {
-+              echo "ERROR: Your database version is greater ($version) than this upgrade supports ($target)!\n";
-+              return EXIT_ERROR;
-+      }
-+      if ($target == $version) {
-+              echo "Database already at version $version. Nothing to upgrade.\n";
-+              return EXIT_OK;
-+      }
-+
-+      echo "Upgrading database to version $target\n";
-+      for ($i = $version + 1; $i <= $target; $i++) {
-+              if (empty($versions[$i])) {
-+                      echo "ERROR: patch $i is not recorded in upgrade script.\n";
-+                      return EXIT_ERROR;
-+              }
-+              $patch = APP_SQL_PATCHES_PATH . '/' . $versions[$i];
-+              echo "Checking patch $patch\n";
-+              if (!file_exists($patch)) {
-+                      echo "ERROR: Patch file doesn't exist\n";
-+                      return EXIT_ERROR;
-+              }
-+              require $patch;
-+              $func = "db_patch_$i";
-+              if (!function_exists($func)) {
-+                      echo "ERROR: Patch did not define '$func' function\n";
-+                      return EXIT_ERROR;
-+              }
-+              $patchset = $func();
-+              echo "Applying patch ", $i, ": ", count($patchset), " queries\n";
-+              apply_db_changes($patchset);
-+              db_query("UPDATE %TABLE_PREFIX%version SET ver_version=$i");
-+      }
-+
-+      return EXIT_OK;
-+}
-+
-+if (php_sapi_name() != 'cli') {
-+      echo "<pre>\n";
-+}
-+
-+$ret = patch_database();
-+
-+if (php_sapi_name() != 'cli') {
-+      echo "</pre>\n";
-+}
-+
-+exit($ret);
This page took 1.075899 seconds and 4 git commands to generate.