PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

ajax
img
customer_migration_functions.inc.php
wget 'https://lists2.roe3.org/hesk/_install/customer_migration_functions.inc.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

$hesk_settings['db_failure_response'] = 'json';

hesk_dbConnect();

function customer_migration_get_customers_to_migrate() {
    global $hesk_settings;

    // Check if we've already done this.  If so, hand it back
    $already_done_rs = hesk_dbQuery("SELECT 1 FROM INFORMATION_SCHEMA.TABLES 
         WHERE TABLE_SCHEMA = '".hesk_dbEscape($hesk_settings['db_name'])."' 
         AND TABLE_NAME = '".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers'");
    if (hesk_dbNumRows($already_done_rs) > 0) {
        $customer_count_rs = hesk_dbQuery("SELECT COUNT(1) AS `total_count` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` UNION ALL
            SELECT COUNT(1) AS `total_count` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` WHERE `processed` = 0");
        $customerCount = -1;
        $amountToProcess = -1;
        $index = 0;
        while ($row = hesk_dbFetchAssoc($customer_count_rs)) {
            if ($index === 0) {
                $customerCount = $row['total_count'];
            } else {
                $amountToProcess = $row['total_count'];
            }
            $index++;
        }

        return [
            'customerCount' => $customerCount,
            'amountToProcess' => $amountToProcess
        ];
    }

    /*
     * 0. Create work table
     * 1. Grab distinct customers, based on their newest info (excluding tickets with multiple emails and no emails)
     * 2. Store customer information into work table
     * 3. Grab multi-email tickets
     * 4. Create new work table records or assign to existing work table record
     * 5. Grab no-email tickets (treat each distinct name as separate)
     * 6. Insert work table records
     */
    hesk_dbQuery("CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` (
        `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
        `fake_customer_id` int not null,
        `name` varchar(255) not null COLLATE utf8_unicode_ci,
        `email` varchar(255) not null COLLATE utf8_unicode_ci,
        `language` varchar(50) null,
        `processed` smallint(1) not null,
        PRIMARY KEY (`id`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci");

    // Single-email customers
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` (`fake_customer_id`, `name`, `email`, `language`, `processed`)
        SELECT `user_info`.`id`, TRIM(`u_name`), TRIM(`u_email`), `user_info`.`language`, 0
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `user_info`
        INNER JOIN (
            SELECT MAX(`id`) AS id, TRIM(`u_name`), TRIM(`u_email`)
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
            GROUP BY TRIM(`u_name`), TRIM(`u_email`)
        ) AS `newest_ticket`
            ON `user_info`.`id` = `newest_ticket`.`id`
        WHERE (`u_email` NOT LIKE '%,%' AND TRIM(`u_email`) <> '')
        ORDER BY `user_info`.`id`");

    // Multi-email customers
    $multi_rs = hesk_dbQuery("SELECT `user_info`.`id`, TRIM(`u_name`) AS `name`, TRIM(`u_email`) AS `email`, `language`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `user_info`
        INNER JOIN (
            SELECT MAX(`id`) AS id, TRIM(`u_name`), TRIM(`u_email`)
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
            GROUP BY TRIM(`u_name`), TRIM(`u_email`)
        ) AS `newest_ticket`
            ON `user_info`.`id` = `newest_ticket`.`id`
        WHERE `u_email` LIKE '%,%'
        ORDER BY `id`");
    while ($row = hesk_dbFetchAssoc($multi_rs)) {
        $split_emails = explode(',', $row['email']);
        $first = true;
        foreach ($split_emails as $email) {
            $name = $first ? $row['name'] : '';
            $customer_exists_rs = hesk_dbQuery("SELECT `fake_customer_id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers`
             WHERE `email` = '".hesk_dbEscape($email)."' 
             AND `name` = '".hesk_dbEscape($name)."'");

            if (hesk_dbNumRows($customer_exists_rs) === 0) {
                // No customer record; insert one
                $language = $row['language'] ? "'".hesk_dbEscape($row['language'])."'" : 'NULL';
                hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` (`fake_customer_id`, `name`, `email`, `language`, `processed`)
                    VALUES (".intval($row['id']).", '".hesk_dbEscape($name)."', '".hesk_dbEscape($email)."', ".$language.", 0)");
            }

            $first = false;
        }
    }

    // No email customers
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` (`fake_customer_id`, `name`, `email`, `language`, `processed`)
        SELECT `user_info`.`id`, TRIM(`u_name`), '', `user_info`.`language`, 0
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `user_info`
        INNER JOIN (
            SELECT MAX(`id`) AS id, TRIM(`u_name`), TRIM(`u_email`)
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
            GROUP BY TRIM(`u_name`), TRIM(`u_email`)
        ) AS `newest_ticket`
            ON `user_info`.`id` = `newest_ticket`.`id`
        WHERE TRIM(COALESCE(`u_email`, '')) = ''
        ORDER BY `user_info`.`id`");

    // Grab the total number of customers
    $total_count_rs = hesk_dbQuery("SELECT COUNT(1) AS `cnt` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers`");
    $total_count = hesk_dbFetchAssoc($total_count_rs);

    return [
        'customerCount' => $total_count['cnt'],
        'amountToProcess' => $total_count['cnt']
    ];
}

function customer_migration_create_customer_batch() {
    global $hesk_settings;

    $next_batch = hesk_dbQuery("SELECT `id`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers`
        WHERE `processed` = 0
        ORDER BY `id`
        LIMIT 100");
    $ids = [];
    while ($row = hesk_dbFetchAssoc($next_batch)) {
        $ids[] = intval($row['id']);
    }

    if (count($ids) === 0) {
        return false;
    }

    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (`pass`, `name`, `email`,
        `language`, `verified`, `verification_token`, `mfa_enrollment`, `mfa_secret`)
        SELECT NULL, `temp_customer`.`name`, `temp_customer`.`email`, `temp_customer`.`language`, 350, NULL, 0, NULL
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers` AS `temp_customer`
        WHERE `id` IN (".implode(',', $ids).")");
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_customers`
    SET `processed` = 1
    WHERE `id` IN (".implode(',', $ids).")");

    return true;
}

function customer_migration_associate_tickets() {
    global $hesk_settings;

    $new_customers_rs = hesk_dbQuery("SELECT COUNT(1) AS `cnt`,
        IF(EXISTS (SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `verified` = 350 AND `email` <> ''), 1, 0) AS `has_email`,
        IF(EXISTS (SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `verified` = 350 AND `email` = ''), 1, 0) AS `has_no_email`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `verified` = 350");
    $customer_stats = hesk_dbFetchAssoc($new_customers_rs);
    $migrated_count = intval($customer_stats['cnt']);

    // Insert requester records (with email, no multi-email)
    if (intval($customer_stats['has_email']) === 1) {
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`)
        SELECT `ticket`.`id`, `customer`.`id`, 'REQUESTER'
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
            ON TRIM(`ticket`.`u_email`) = `customer`.`email`
        WHERE `customer`.`verified` = 350
            AND `ticket`.`u_email` <> ''
            AND `ticket`.`u_email` NOT LIKE '%,%'
            AND `ticket`.`u_name` = `customer`.`name`");

        // Insert requester records (with email, multi-email)
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`)
        SELECT `ticket`.`id`, `customer`.`id`, 'REQUESTER'
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
            ON TRIM(`ticket`.`u_email`) LIKE CONCAT(`customer`.`email`, ',%')
        WHERE `customer`.`verified` = 350
            AND `ticket`.`u_email` LIKE '%,%'
            AND `ticket`.`u_name` = `customer`.`name`");

        // Insert follower records (with email)
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`) 
        SELECT `ticket`.`id`, MIN(customer.id) AS customer_id, 'FOLLOWER'
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
            ON (`ticket`.`u_email` LIKE CONCAT('%,', `customer`.`email`, ',%')
                OR `ticket`.`u_email` LIKE CONCAT('%,', `customer`.`email`))
        WHERE `customer`.`verified` = 350
            AND `customer`.`email` <> ''
            AND `ticket`.`u_email` LIKE '%,%'
        GROUP BY `ticket`.`id`, `customer`.`email`");
    }

    if (intval($customer_stats['has_no_email']) === 1) {
        // Insert requester records (no email)
        // I'm intentionally using a STRAIGHT_JOIN here.  For some reason, an INNER JOIN uses a really bad execution plan
        // which causes the query to take minutes instead of a few seconds (worst-case)
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`)
            SELECT `ticket`.`id`, `customer`.`id`, 'REQUESTER'
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
            STRAIGHT_JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket`
              ON TRIM(`ticket`.`u_name`) = `customer`.`name`
              AND TRIM(`ticket`.`u_email`) = `customer`.`email`
            WHERE `customer`.`verified` = 350
              AND TRIM(`ticket`.`u_email`) = ''
              AND `ticket`.`u_name` = `customer`.`name`");
    }

    // Update replies for requesters
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `replies`
            INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer`
                ON `replies`.`replyto` = `ticket_to_customer`.`ticket_id`
                AND `ticket_to_customer`.`customer_type` = 'REQUESTER'
            INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
                ON `ticket_to_customer`.`customer_id` = `customer`.`id`
            SET `replies`.`customer_id` = `ticket_to_customer`.`customer_id`
            WHERE `customer`.`verified` = 350
                AND `staffid` = 0");

    // Mark customers as migrated
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        SET `verified` = 0
        WHERE `verified` = 350");

    return $migrated_count;
}

function customer_migration_map_customer_to_tickets($customer, $tickets, $customer_type) {
    global $hesk_settings;
    $BATCH_SIZE = 1000;

    // Insert ticket/customer mappings
    for ($i = 0; $i < count($tickets); $i += $BATCH_SIZE) {
        $ticket_sublist = array_slice($tickets, $i, $BATCH_SIZE);
        $ticket_ids = [];

        $sql = "INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`) ";
        $ticket_query_parts = [];
        foreach ($ticket_sublist as $ticket) {
            $ticket_query_parts[] = "SELECT ".intval($ticket['id']).", ".intval($customer['id']).", '".hesk_dbEscape($customer_type)."'";
            $ticket_ids[] = intval($ticket['id']);
        }
        $sql .= implode(' UNION ', $ticket_query_parts);
        hesk_dbQuery($sql);
    }

    // Update customer_id on non-staff replies
    if ($customer_type === 'REQUESTER') {
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `replies`
            INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer`
                ON `replies`.`replyto` = `ticket_to_customer`.`ticket_id`
            SET `replies`.`customer_id` = `ticket_to_customer`.`customer_id`
            WHERE `replies`.`replyto` IN (".implode(',', $ticket_ids).")
                AND `staffid` = 0");
    }
}
hesk.png
wget 'https://lists2.roe3.org/hesk/_install/hesk.png'
View Content
hesk_javascript.js
wget 'https://lists2.roe3.org/hesk/_install/hesk_javascript.js'
View Content
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

function hesk_insertTag(tag) {
var text_to_insert = '%%'+tag+'%%';
hesk_insertAtCursor(document.form1.msg, text_to_insert);
document.form1.message.focus();
}

function hesk_insertAtCursor(myField, myValue) {
if (document.selection) {
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
else if (myField.selectionStart || myField.selectionStart == '0') {
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
} else {
myField.value += myValue;
}
}

function hesk_changeAll(myID) {
  var d = document.form1;
  var setTo = myID.checked ? true : false;

  for (var i = 0; i < d.elements.length; i++)
  {
    if(d.elements[i].type == 'checkbox' && d.elements[i].name != 'checkall')
    {
     d.elements[i].checked = setTo;
    }
  }
}

function hesk_attach_disable(ids) {
 for($i=0;$i<ids.length;$i++) {
      if (ids[$i]=='c11'||ids[$i]=='c21'||ids[$i]=='c31'||ids[$i]=='c41'||ids[$i]=='c51') {
            document.getElementById(ids[$i]).checked=false;
      }
      document.getElementById(ids[$i]).disabled=true;
 }
}

function hesk_attach_enable(ids) {
 for($i=0;$i<ids.length;$i++) {
      document.getElementById(ids[$i]).disabled=false;
 }
}

function hesk_attach_toggle(control,ids) {
 if (document.getElementById(control).checked) {
     hesk_attach_enable(ids);
 } else {
     hesk_attach_disable(ids);
 }
}

function hesk_window(PAGE,HGT,WDT)
{
 var HeskWin = window.open(PAGE,"Hesk_window","height="+HGT+",width="+WDT+",menubar=0,location=0,toolbar=0,status=0,resizable=1,scrollbars=1");
 HeskWin.focus();
}

function hesk_toggleLayerDisplay(nr) {
        if (document.all)
                document.all[nr].style.display = (document.all[nr].style.display == 'none') ? 'block' : 'none';
        else if (document.getElementById)
                document.getElementById(nr).style.display = (document.getElementById(nr).style.display == 'none') ? 'block' : 'none';
}

function hesk_confirmExecute(myText) {
         if (confirm(myText))
         {
          return true;
         }
         return false;
}

function hesk_deleteIfSelected(myField,myText) {
         if(document.getElementById(myField).checked)
         {
          return hesk_confirmExecute(myText);
         }
}

function hesk_rate(url,element_id)
{
        if (url.length==0)
        {
                return false;
        }

        var element = document.getElementById(element_id);

        xmlHttp=GetXmlHttpObject();
        if (xmlHttp==null)
        {
                alert ("Your browser does not support AJAX!");
                return;
        }

        xmlHttp.open("GET",url,true);

        xmlHttp.onreadystatechange = function()
        {
         if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
         {
          element.innerHTML = xmlHttp.responseText;
         }
        }

        xmlHttp.send(null);
}

function stateChanged()
{
        if (xmlHttp.readyState==4)
        {
                document.getElementById("rating").innerHTML=xmlHttp.responseText;
        }
}

function GetXmlHttpObject()
{
        var xmlHttp=null;
        try
        {
                // Firefox, Opera 8.0+, Safari
                xmlHttp=new XMLHttpRequest();
        }
        catch (e)
        {
                // Internet Explorer
                try
                {
                        xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
                }
                catch (e)
                {
                        xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
                }
        }
        return xmlHttp;
}

var heskKBquery = '';
var heskKBfailed = false;

function hesk_suggestKB()
{
 var d = document.form1;
 var s = d.subject.value;
 var m = d.message.value;
 var element = document.getElementById('kb_suggestions');

 if (s != '' && m != '' && (heskKBquery != s + " " + m || heskKBfailed == true) )
 {
  element.style.display = 'block';
  var params = "p=1&" + "q=" + encodeURIComponent( s + " " + m );
  heskKBquery = s + " " + m;

   xmlHttp=GetXmlHttpObject();
   if (xmlHttp==null)
   {
     return;
   }

   xmlHttp.open('POST','suggest_articles.php',true);
   xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

   xmlHttp.onreadystatechange = function()
   {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
      {
       element.innerHTML = xmlHttp.responseText;
       heskKBfailed = false;
      }
      else
      {
       heskKBfailed = true;
      }
   }

   xmlHttp.send(params);
 }

 setTimeout(function() { hesk_suggestKB(); }, 2000);

}

function hesk_suggestKBsearch(isAdmin)
{
 var d = document.searchform;
 var s = d.search.value;
 var element = document.getElementById('kb_suggestions');

 if (isAdmin)
 {
  var path = 'admin_suggest_articles.php';
 }
 else
 {
  var path = 'suggest_articles.php';
 }

 if (s != '' && (heskKBquery != s || heskKBfailed == true) )
 {
  element.style.display = 'block';
  var params = "q=" + encodeURIComponent( s );
  heskKBquery = s;

   xmlHttp=GetXmlHttpObject();
   if (xmlHttp==null)
   {
     return;
   }

   xmlHttp.open('POST', path, true);
   xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

   xmlHttp.onreadystatechange = function()
   {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
      {
       element.innerHTML = unescape(xmlHttp.responseText);
       heskKBfailed = false;
      }
      else
      {
       heskKBfailed = true;
      }
   }

   xmlHttp.send(params);
 }

 setTimeout(function() { hesk_suggestKBsearch(isAdmin); }, 2000);
}

function hesk_suggestEmail(emailField, displayDiv, padDiv, isAdmin, allowMultiple)
{
 var email = document.getElementById(emailField).value;
 var element = document.getElementById(displayDiv);

 if (isAdmin)
 {
  var path = '../suggest_email.php';
 }
 else
 {
  var path = 'suggest_email.php';
 }

 if (email != '')
 {
  var params = "e=" + encodeURIComponent(email) + "&ef=" + encodeURIComponent(emailField) + "&dd=" + encodeURIComponent(displayDiv) + "&pd=" + encodeURIComponent(padDiv);

  if (allowMultiple)
  {
   params += "&am=1";
  }

   xmlHttp=GetXmlHttpObject();
   if (xmlHttp==null)
   {
     return;
   }

   xmlHttp.open('POST', path, true);
   xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

   xmlHttp.onreadystatechange = function()
   {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
      {
       element.innerHTML = unescape(xmlHttp.responseText);
       element.style.display = 'block';
      }
   }

   xmlHttp.send(params);
 }
}

function hesk_btn(Elem, myClass)
{
        Elem.className = myClass;
}

function hesk_checkPassword(password)
{

    var numbers = "0123456789";
    var lowercase = "abcdefghijklmnopqrstuvwxyz";
    var uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var punctuation = "!.@$#*()%~<>{}[]";

    var combinations = 0;

    if (hesk_contains(password, numbers) > 0) {
        combinations += 10;
    }

    if (hesk_contains(password, lowercase) > 0) {
        combinations += 26;
    }

    if (hesk_contains(password, uppercase) > 0) {
        combinations += 26;
    }

    if (hesk_contains(password, punctuation) > 0) {
        combinations += punctuation.length;
    }

    var totalCombinations = Math.pow(combinations, password.length);
    var timeInSeconds = (totalCombinations / 200) / 2;
    var timeInDays = timeInSeconds / 86400
    var lifetime = 365000;
    var percentage = timeInDays / lifetime;

    var friendlyPercentage = hesk_cap(Math.round(percentage * 100), 98);

    if (friendlyPercentage < (password.length * 5)) {
        friendlyPercentage += password.length * 5;
    }

    var friendlyPercentage = hesk_cap(friendlyPercentage, 98);

    var progressBar = document.getElementById("progressBar");
    progressBar.style.width = friendlyPercentage + "%";

    if (percentage > 1) {
        // strong password
        progressBar.style.backgroundColor = "#3bce08";
        return;
    }

    if (percentage > 0.5) {
        // reasonable password
        progressBar.style.backgroundColor = "#ffd801";
        return;
    }

    if (percentage > 0.10) {
        // weak password
        progressBar.style.backgroundColor = "orange";
        return;
    }

    if (percentage <= 0.10) {
        // very weak password
        progressBar.style.backgroundColor = "red";
        return;
    }

}

function hesk_cap(number, max) {
    if (number > max) {
        return max;
    } else {
        return number;
    }
}

function hesk_contains(password, validChars) {

    count = 0;

    for (i = 0; i < password.length; i++) {
        var char = password.charAt(i);
        if (validChars.indexOf(char) > -1) {
            count++;
        }
    }

    return count;
}

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 getCookie(name)
{
        var dc = document.cookie;
        var prefix = name + "=";
        var begin = dc.indexOf("; " + prefix);
        if (begin == -1) {
                begin = dc.indexOf(prefix);
                if (begin != 0) return null;
        } else {
                begin += 2;
        }
        var end = document.cookie.indexOf(";", begin);
        if (end == -1) {
                end = dc.length;
        }
        return unescape(dc.substring(begin + prefix.length, end));
}

function deleteCookie(name, path, domain)
{
        if (getCookie(name)) {
                document.cookie = name + "=" +
                        ((path) ? "; path=" + path : "") +
                        ((domain) ? "; domain=" + domain : "") +
                        "; expires=Thu, 01-Jan-70 00:00:01 GMT";
        }
}

//region Customer Migration
function getCustomerCount() {
    $.ajax({
        url: 'ajax/customer_migrations.php?action=intro',
        method: 'GET',
        success: function(data) {
            var customerCount = 0;
            try {
                customerCount = JSON.parse(data).numberOfCustomers;
                $('#intro-loader').hide();
                $('#customer-intro-count').text(customerCount);
                $('#migrating').find('#count-total').text(customerCount);
                $('#continue-block').show();
            } catch (e) {
                alert('An error occurred when attempting to fetch the number of customers to migrate.');
                console.error(e);
            }
        },
        error: function(err) {
            try {
                var errorBody = JSON.parse(err.responseText);
                alert(errorBody.title + ':\n' + errorBody.message);
            } catch (e) {
                alert(err.responseText);
            }
            console.error(err.responseText);
        }
    })
}
function beginCustomerMigration() {
    $('#intro').hide();
    $('#migrating').show();
    doMigrationAjax();
}

function doMigrationAjax() {
    $.ajax({
        url: 'ajax/customer_migrations.php?action=migrate',
        method: 'GET',
        success: function(data) {
            var $migratingDiv = $('#migrating');
            var migratedCount = parseInt($('#count-completed').text(), 10);
            try {
                var batchCount = JSON.parse(data).migratedCount;
                migratedCount += batchCount;

                $('#count-completed').text(migratedCount);
                var completedPercentage = (migratedCount / parseInt($migratingDiv.find('#count-total').text())) * 100;
                $migratingDiv.find('.progress-bar').css('width', completedPercentage + '%');

                if (batchCount === 0) {
                    dropOldColumns();
                } else {
                    doMigrationAjax();
                }
            } catch (e) {
                alert('An error occurred when attempting to migrate customers.');
                console.error(e);
            }
        },
        error: function(err) {
            try {
                var errorBody = JSON.parse(err.responseText);
                alert(errorBody.title + ':\n' + errorBody.message);
            } catch (e) {
                alert(err.responseText);
            }
            console.error(err.responseText);
        }
    });
}

function dropOldColumns() {
    $.ajax({
        url: 'ajax/customer_migrations.php?action=cleanup',
        method: 'GET',
        success: function(data) {
            $('#migrating').hide();
            $('#complete').show();
        },
        error: function(err) {
            try {
                var errorBody = JSON.parse(err.responseText);
                alert(errorBody.title + ':\n' + errorBody.message);
            } catch (e) {
                alert(err.responseText);
            }
            console.error(err.responseText);
        }
    })
}
//endregion
hesk_style.css
wget 'https://lists2.roe3.org/hesk/_install/hesk_style.css'
View Content
body {
	background-color: #f3fef4;
	color: black;
	font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	margin-left:0;
	margin-right:0;
	margin-top:0;
	margin-bottom:0;
}

table.enclosing {
	background-color:#FFFFFF;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	width: 960px;
}

td {
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	text-align: left;
}

a img {
	vertical-align: bottom;
	border : none;
}

td.white {
	background-color: #FFFFFF;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;	
}

td.admin_white {
	background-color: #FFFFFF;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	border: #d1dceb 1px solid;
	padding: 1px;	
}

td.admin_gray {
	background-color: #f5fffa;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	border: #d1dceb 1px solid;
	padding: 1px;
}

td.notes {
	background-color: #fffbf2;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	border: #ffe6b8 1px solid;
}

th.tDetailsHead {
	background-color: #F7F7F7;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	font-weight: normal;
	text-align: center;
}

td.tDetailsBody {
	background-color: #FFFFFF;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	border: #F7F7F7 1px solid;
	text-align: center;
}

.small {
	font-size: 11px;
}

.smaller {
	font-size: 10px;
}

a {
	color : Blue;
	text-decoration : underline;
}

a:hover {
	color : Red;
	text-decoration : none;
}

a.smaller {
	font-size: 10px;
	color : Blue;
	text-decoration : underline;
}

a.smaller:hover {
	font-size: 10px;
	color : Red;
	text-decoration : none;
}

table.white {
	background-color: #ffffff ;
	color : #23559C;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	border-collapse: collapse;
}

table.white th {
	background-color: #ffffff;
	color : #23559C;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	border: #b2babd 1px solid;
	border-collapse: collapse;
	background-image: url(img/tableheader.jpg);
	background-repeat: no-repeat;
	background-position: left top;
}

table.white th a {
	color : #23559C;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
}

table.white td {
	height: 25px;
	padding-left: 5px;
}

.noborder {
border: none;
}

h3 {
	color : #74804e;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 13px;
	font-weight: bold;
	margin: 0px;
}

.important {
	color: Red;
}

.medium {
	color: #FF9900;
}

input {
	font-size: 12px;
	font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
	color:#000000;
}

input.button {
	font-size: 12px;
	font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
	color:#000000;
	background-color:#FFFFFF;
}

select {
	font-size: 12px;
	height: 20px;
}

header a img {border: none; text-decoration: none !important;} 

.open {color : #FF0000;}
.waitingreply {color : #FF9933;}
.replied {color : #0000FF;}
.resolved {color : #008000;}
.allbutresolved {color : #000000;}

.success {color : #008000;}
.error {color : #FF0000;}
.notice {color : #FF9900}

#ok {
	border: 1px solid #679800;
}

#error {
	border: 1px solid #ba3225;
}

.rate {
	color: #666666;
	text-align: right;
	font-style: italic; 
}

a.article {
	color : Black;
	text-decoration: none;
	font-size: 11px;
}

a.article:hover {
	color : Red;
	text-decoration : none;
}

.article_list {
	font-size: 11px;	
}

fieldset {
	margin-bottom: 6px; 
	border: 1px SOLID #267DDC;
	padding: 4px;
	background-color:white;
	position:relative;
	display:block;
	padding: 15px 10px 10px 10px;
	margin:20px 0px 20px 0px;
}

legend {
	background-image: url(img/bluebtn.png);
	background-repeat: no-repeat;
	color: #172901;
	border: 1px solid #267DDC;
	height: 10px;
	font-size: 10px;
	font-weight:bold;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	display:block;
	width: auto;
	padding:5px 5px;
	position:relative;
	width:130px;	
}

/*newly introduced styles (version 2.0)*/

.header {
	width: 100%;
	background-color: #74a62b;
	color : #ffffff;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	text-align: center;
	background-image: url(img/header.png);
	background-repeat: repeat-x;
	height: 57px;
	padding: 0px;
	margin: 0px;
}

.header td {
text-align: center;
vertical-align: middle;
color:#FFFFFF;
}
.header a, .header a:link, .header a:active, .header a:visited {
	color : #ffffff;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	text-align: center;
}
.header a:hover {}

.headersm {
	width: 100%;
	background-color: #669933;
	color : #ffffff;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	text-align: left;
	background-image: url(img/headerbgsm.jpg);
	background-repeat: repeat-x;
	height: 25px;
	padding: 0px;
	margin: 0px;
	font-weight:bold;
	padding-left: 20px;
}

hr {
	border: none;
	border-bottom: 1px dotted #adbac3;
	width: 100%;
	padding-top: 10px;
	margin-bottom: 10px;
	height: 1px;
}

.greenbutton {
	background-image: url(img/greenbtn.jpg);
	background-repeat: no-repeat;
	text-align: center;
	color: #FFFFFF;
	border: 1px solid #527234;
	font-size: 10px;
	font-weight:bold;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	height: 19px;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	margin-right: 2px
}

.greenbuttonover {
	background-image: url(img/greenbtnover.gif);
	background-repeat: no-repeat;
	text-align: center;
	color: #FFFFFF;
	border: 1px solid #527234;
	font-size: 10px;
	font-weight:bold;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	height: 19px;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	margin-right: 2px
}

.orangebutton {
	background-image: url(img/orangebtn.jpg);
	background-repeat: no-repeat;
	text-align: center;
	color: #660000;
	border: 1px solid #bf6628;
	height: 20px;
	font-size: 10px;
	font-weight:bold;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	margin-right: 2px;
	text-decoration: none;
	margin-top: 0px;	
}

.orangebuttonover {
	background-image: url(img/orangebtnover.gif);
	background-repeat: no-repeat;
	text-align: center;
	color: #660000;
	border: 1px solid #bf6628;
	height: 20px;
	font-size: 10px;
	font-weight:bold;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	margin-right: 2px;
	text-decoration: none;
	margin-top: 0px;		
}

.bluebutton {
	background-image: url(img/bluebtn.png);
	background-repeat: no-repeat;
	text-align: center;
	color: #660000;
	border: 1px solid #5b79a3;
	height: 20px;
	font-size: 10px;
	font-weight:bold;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	text-decoration: none;
	margin-right: 2px;
	padding-top: 2px;
}


/*styles for roundcorners tables*/

.roundcornersleft {
	width: 7px;
	background-image: url(img/roundcornerslm.jpg);
	background-repeat: repeat-y;
	background-position: left;
}
.roundcornersright {
	width: 7px;
	background-image: url(img/roundcornersrm.jpg);
	background-repeat: repeat-y;
	background-position: right;
}
.roundcornerstop {
	height: 7px;
	background-image: url(img/roundcornerst.jpg);
	background-repeat: repeat-x;
	background-position: top;
}
.roundcornersbottom {
	height: 7px;
	background-image: url(img/roundcornersb.jpg);
	background-repeat: repeat-x;
	background-position: bottom;
}
.ticketrow {
	background-color: #f5fffa;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	border: #748aaf 1px dotted;
	padding: 6px;
}
.ticketalt {
	background-color: #ffffff;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;
	padding: 6px;
}

.tickettd {
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 12px;	
}

.subheaderrow {
	background-color: #ffffff;
	color : #23559C;
	border: #23559C solid 1px;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	border-collapse: collapse;
	background-image: url(img/tableheader.jpg);
	background-repeat: no-repeat;
	background-position: left top;
	height: 25px;
	padding-left: 20px;
	padding-right: 20px;
	padding-top: 5px;
	}
	
.homepageh3, .section {
	color: #74804e;
	font-weight: bold;
}

/*newly introduced styles (version 2.2)*/	

.large {
	font-size:14px;
}

.largebold {
	font-size:14px;
	font-weight:bold;
}

.assignedyou {
	color: red;
}

.assignedother {
	color: green;
}

div.error {
	border: 1px solid #cd0a0a; 
	background: #fef1ec; 
	color: #cd0a0a;
	padding: 10px;
}

div.success {
	border: 1px solid #18760f; 
	background: #e9ffdb; 
	color: #363636;
	padding: 10px;
}

div.notice {
	border: 1px solid #fcefa1; 
	background: #fff9de; 
	color: #363636;
	padding: 10px;
	vertical-align: middle;
}

.admin_green {
	background-color: #e3ffd0;
	font-weight: bold;
}

.admin_red {
	background-color: #fef1ec;
	font-weight: bold;
}

.borderBottom {
	border-bottom: silver 1px dashed;
}

.borderTop {
	border-top: silver 1px dashed;
}

.alignTop {
	vertical-align: top;
}

.alignMiddle {
	vertical-align: middle;
}

.alignBottom {
	vertical-align: bottom;
}

hr.dashed  {
	border: none 0; 
	border-top: 1px dashed silver;
	height: 1px;
}

/* newly introduced styles (version 2.3) */	

h1 {
	color : #74804e;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 13px;
	font-weight: bold;
	margin: 0px;
}

.critical {
	color: #9400d3;
	font-weight: bold;
} 

td.admin_critical {
	background-color: #fff0ff;
	color : #000000;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	border: 1px solid #ff66ff;
	padding: 1px;
}

.isError {
	color: black;
	background-color: #fff9f7;
	border: 1px solid red;
}

.isNotice {
	color: black;
	border: 1px solid orange; 
	background: #fbf9ee;
}
	
.optionWhiteON{
	padding:2px;
	border:1px dotted silver;
	background-color: #b0e0e6;	
}

.optionWhiteOFF {
	padding:2px;
	border:1px dotted silver;
	background-color: white;
}

.optionBlueON {
	padding:2px;
	border:1px dotted gray;
	background-color: #b0e0e6;	
}

.optionBlueOFF {
	padding:2px;
	border:1px dotted gray;
	background-color: #f5fffa;
}

.optionWhiteNbON{
	border: none; 
	background-color: #b0e0e6;
	vertical-align: text-bottom;
}

.optionWhiteNbOFF {
	border: none;
	background-color: white;
	vertical-align: text-bottom;
}

.kbCatListON {
	background-color: #fcefa1;
}

.kbCatListOFF {
	background-color: white;	
}

div.progress-container {
	border: 1px solid #ccc;
	width: 100px;
	margin: 2px 5px 2px 0;
	padding: 1px;
	float: left;
	background: white;
}

div.progress-container > div {
	background-color: #ACE97C;
	height: 12px
}

.black {color: black;}
.inprogress {color : #006400;}
.onhold {color : #000000;}

div.online {
	border: 1px solid #e5e8ff; 
	background: #ffffff; 
	color: #000000;
	padding: 0px;
	vertical-align: middle;
}

span.online {
	font-size: 10px;
	white-space:nowrap;
}

/* newly introduced styles (version 2.4) */	

.orangebuttonsec {
	background-image: url(img/orangebtnsec.jpg);
	background-repeat: no-repeat;
	text-align: center;
	color: #660000;
	border: 1px solid #bf6628;
	height: 20px;
	font-size: 10px;
	font-weight:normal;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	margin-right: 2px;
	text-decoration: none;
	margin-top: 0px;	
}

.orangebuttonsecover {
	background-image: url(img/orangebtnover.gif);
	background-repeat: no-repeat;
	text-align: center;
	color: #660000;
	border: 1px solid #bf6628;
	height: 20px;
	font-size: 10px;
	font-weight:normal;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	background-position: left top;
	padding-left: 6px;
	padding-right: 6px;
	margin-left: 2px;
	margin-right: 2px;
	text-decoration: none;
	margin-top: 0px;		
}

td.admin_yellow {
	background-color: #ffffe0;
	color : #4a5571;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 11px;
	border: #d1dceb 1px solid;
	padding: 1px;	
}

/* New styles in HESK version 2.5 */

.kb_published {
	color: #009900;
}

.kb_private {
	color: #4a5571;
}

.kb_draft {
	color: #cc9933;
}

.searchbutton {
	cursor:pointer;
	width:70px;
	height:27px;
	text-indent:-999px;
	text-transform: capitalize;
	color: transparent;
	background: url(img/ico-search.png) no-repeat #4d90fe center;
	border: 1px solid #3079ED;
	-moz-border-radius: 2px;
	-webkit-border-radius: 2px;	
}

.searchbutton:hover {
	background: url(img/ico-search.png) no-repeat center #357AE8;
	border: 1px solid #2F5BB7;
}

.searchfield {
	width:400px;
	height:21px;
	font-size:14px;
	text-indent:2px; 
	vertical-align: bottom;
}

.sbsmall {
	width:50px; 
	height:22px; 
	margin-top:2px;
}

.sfsmall {
	width:200px; 
	height:16px; 
	font-size:12px; 
	margin-top:2px;
}

/* New styles in HESK version 2.6 */

div.info {
	border: 1px solid #9acfea; 
	background: #d9edf7; 
	color: #363636;
	padding: 10px;
	vertical-align: middle;
}

div.none {
	color: #363636;
	padding: 10px;
	vertical-align: middle;
}

/* New styles in HESK version 2.7 */

div.select_category 
{
	min-width: 50%;
	min-height: 300px;
	display: inline-block;
	text-align:left;
	margin-top: 10px;
}

#select_category {
	border: 1px solid #111;
	background: transparent;
	width: 100%;
	padding: 5px 35px 5px 5px;
	font-size: 14px;
	border: 1px solid #ccc;
	height: 34px;
} 

#ul_category {
	list-style-type: none;
	margin: 0;
	padding: 0;
}

#ul_category li {
	border: 1px solid #d1d5d7;
	border-top: none;
	border-radius: 2px;
}

#ul_category li:first-child {
	border-top: 1px solid #d1d5d7;
}

#ul_category li a {
	display: block;
	font-size: 14px;
	padding: 0.75em 0.75em;
	text-decoration: none;
	transition: all 0.12s ease;
	word-wrap: break-word;
}
 
#ul_category li a:hover {
	color: black;
	background-color: #e9ffdb;
}

select.multiple {
	font-size: 12px;
	height: auto;
}

/* New styles in HESK version 2.8 */

#hesk_nav{
	list-style:none;
	float:right;
	/* Bring the nav above everything else--uncomment if needed.
	position:relative;
	z-index:5;
	*/
	margin: 0px;
}

#hesk_nav li{
	float:left;
	margin-right:4px;
	position:relative;
}

#hesk_nav a{
	display:block;
	background-color:#ffffff;
	-moz-border-radius:3px;
	-webkit-border-radius:3px;
	border-radius:3px;
	border:1px solid #dcdcdc;
	cursor:pointer;
	color:#666666;
	font-size:12px;
	padding:4px 10px;
	text-decoration:none;
}
#hesk_nav a:hover{
	color: red;
	background:#f6f6f6;
}

/*--- DROPDOWN ---*/
#hesk_nav ul{
	background:#fff; /* Adding a background makes the dropdown work properly in IE7+. Make this as close to your page's background as possible (i.e. white page == white background). */
	background:rgba(255,255,255,0); /* But! Let's make the background fully transparent where we can, we don't actually want to see it if we can help it... */
	list-style:none;
	position:absolute;
	left:-9999px; /* Hide off-screen when not needed (this is more accessible than display:none;) */
	z-index:5;
	padding-left: 0px;
	padding-top: 2px;
}
#hesk_nav ul li{
	padding-top:1px; /* Introducing a padding between the li and the a give the illusion spaced items */
	float:none;
}
#hesk_nav ul a{
	white-space:nowrap; /* Stop text wrapping and creating multi-line dropdown items */
	padding:8px 14px;
}
#hesk_nav li:hover ul{ /* Display the dropdown on hover */
	left:auto; /* Bring back on-screen when needed */
	text-align:left;
	right:0;
	margin-right:-10px;
}
#hesk_nav li:hover a{ /* These create persistent hover states, meaning the top-most link stays 'hovered' even when your cursor has moved down the list. */
	background:#f6f6f6;
	text-decoration:none;
}

#hesk_nav li:hover ul a{
	background:#ffffff;
	text-decoration:none;
}

#hesk_nav li:hover ul a{ /* The persistent hover state does however create a global style for links even before they're hovered. Here we undo these effects. */
	text-decoration:none;
}
#hesk_nav li:hover ul li a:hover{ /* Here we define the most explicit hover states--what happens when you hover each individual link. */
	background:#f6f6f6;
}

.notification {
    background-color: #fff;
    box-shadow: 0 4px 8px 0 rgba(0, 41, 89, 0.1);
    padding: 20px 28px 20px 28px;
    position: relative;
    display: block;
    margin-bottom: 24px;
}

.notification::before {
    content: "";
    width: 8px;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
}

.notification.red {
    background-color: #fff5f5;
}

.notification.red::before {
    background-color: #e64342;
}

.notification.orange {
    background-color: #ffffcc;
}

.notification.orange::before {
    background-color: #eeb707;
}

.notification.green {
    background-color: #f0fff4;
}

.notification.green::before {
    background-color: #38bc7d;
}

.notification.blue {
    background-color: #ebf8ff;
}

.notification.blue::before {
    background-color: #002d73;
}

.notification b {
    font-weight: bold;
}

.notification i {
    font-style: italic;
}

.lds-dual-ring {
	display: inline-block;
	width: 40px;
	height: 40px;
}
.lds-dual-ring:after {
	content: " ";
	display: block;
	width: 32px;
	height: 32px;
	margin: 8px;
	border-radius: 50%;
	border: 6px solid #008000;
	border-color: #008000 transparent #008000 transparent;
	animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
	0% {
		transform: rotate(0deg);
	}
	100% {
		transform: rotate(360deg);
	}
}

.progress-values {
	margin: 5px 0;
	text-align: center;
}
install.php
wget 'https://lists2.roe3.org/hesk/_install/install.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','../');

define('INSTALL_PAGE', 'install.php');
require(HESK_PATH . 'install/install_functions.inc.php');

// If no step is defined, start with step 1
if ( ! isset($_SESSION['step']) )
{
    $_SESSION['step']=1;
}
// Check if the license has been agreed to and verify sessions are working
elseif ($_SESSION['step']==1)
{
    $agree = hesk_POST('agree', '');
    if ($agree == 'YES')
    {
		// Are sessions working?
		if ( empty($_SESSION['works']) )
        {
        	hesk_iSessionError();
        }

		// All OK, continue
        $_SESSION['license_agree']=1;
        $_SESSION['step']=2;
    }
    else
    {
        $_SESSION['step']=1;
    }
}

// Test database connection?
if ($_SESSION['step'] == 3 && isset($_POST['dbtest']) )
{
    // Name
    $_SESSION['admin_name'] = hesk_input( hesk_POST('admin_name') );
    if ( strlen($_SESSION['admin_name']) == 0 )
    {
        $_SESSION['admin_name'] = 'Your name';
    }

    // Email
    $_SESSION['admin_email'] = hesk_validateEmail( hesk_POST('admin_email'), 'ERR', 0);
    if ( strlen($_SESSION['admin_email']) == 0 )
    {
        $_SESSION['admin_email'] = 'you@example.com';
    }

	// Username
	$_SESSION['admin_user'] = hesk_input( hesk_POST('admin_user') );
	if ( strlen($_SESSION['admin_user']) == 0 )
	{
		$_SESSION['admin_user'] = 'Administrator';
	}

	// Password
	$_SESSION['admin_pass'] = hesk_input( hesk_POST('admin_pass') );
	if ( strlen($_SESSION['admin_pass']) == 0 )
	{
		$_SESSION['admin_pass'] = substr(str_shuffle("23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"), 0, mt_rand(8,12) );
	}

	// Password hash for the database
	$_SESSION['admin_hash'] = hesk_password_hash($_SESSION['admin_pass']);

	// Test DB connection
	$hesk_db_link = hesk_iTestDatabaseConnection();

    // Generate HESK table names
	$hesk_tables = array(
        $hesk_settings['db_pfix'].'attachments',
        $hesk_settings['db_pfix'].'auth_tokens',
        $hesk_settings['db_pfix'].'banned_emails',
        $hesk_settings['db_pfix'].'banned_ips',
        $hesk_settings['db_pfix'].'categories',
        $hesk_settings['db_pfix'].'custom_fields',
        $hesk_settings['db_pfix'].'custom_statuses',
        $hesk_settings['db_pfix'].'customers',
        $hesk_settings['db_pfix'].'kb_articles',
        $hesk_settings['db_pfix'].'kb_attachments',
        $hesk_settings['db_pfix'].'kb_categories',
        $hesk_settings['db_pfix'].'logins',
        $hesk_settings['db_pfix'].'log_overdue',
        $hesk_settings['db_pfix'].'mail',
        $hesk_settings['db_pfix'].'mfa_backup_codes',
        $hesk_settings['db_pfix'].'mfa_verification_tokens',
        $hesk_settings['db_pfix'].'notes',
        $hesk_settings['db_pfix'].'oauth_providers',
        $hesk_settings['db_pfix'].'oauth_tokens',
        $hesk_settings['db_pfix'].'online',
        $hesk_settings['db_pfix'].'pipe_loops',
        $hesk_settings['db_pfix'].'replies',
        $hesk_settings['db_pfix'].'reply_drafts',
        $hesk_settings['db_pfix'].'reset_password',
        $hesk_settings['db_pfix'].'service_messages',
        $hesk_settings['db_pfix'].'std_replies',
        $hesk_settings['db_pfix'].'tickets',
        $hesk_settings['db_pfix'].'ticket_to_customer',
        $hesk_settings['db_pfix'].'ticket_templates',
        $hesk_settings['db_pfix'].'users',
	);

	// Check if any of the HESK tables exists
	$res = hesk_dbQuery('SHOW TABLES FROM `'.hesk_dbEscape($hesk_settings['db_name']).'`');

	while ($row = hesk_dbFetchRow($res))
	{
		if (in_array($row[0],$hesk_tables))
		{
			hesk_iDatabase(2);
		}
	}

    // Timezone
    $hesk_settings['timezone'] = hesk_input( hesk_POST('timezone') );
    if ( ! in_array($hesk_settings['timezone'], timezone_identifiers_list()) )
    {
        $hesk_settings['timezone'] = 'UTC';
    }

	// All ok, let's save settings
	hesk_iSaveSettings();

	// Now install HESK database tables
	hesk_iTables();

	// And move to step 5 as we don't have to worry about customer account migrations
	$_SESSION['step']=5;
}

// Which step are we at?
switch ($_SESSION['step'])
{
	case 2:
	   hesk_iCheckSetup();
	   break;
	case 3:
	   hesk_iDatabase();
	   break;
    case 4:
	case 5:
	   hesk_iFinish();
	   break;
	default:
	   hesk_iStart();
}


// ******* FUNCTIONS ******* //


function hesk_iFinish()
{
    global $hesk_settings;
    hesk_purge_cache();
    hesk_iHeader();
	?>

	<br />
	<?php hesk_show_success('Congratulations, you have successfully completed HESK database setup!'); ?>

    <h3>Next steps:</h3>

    <ol>

    <li><span style="color:#ff0000">Delete the <b>/install</b> folder from your server!</span><br />&nbsp;</li>

    <li>Remember your login details:<br />

<pre style="font-size: 1.17em">
Username: <span style="color:red; font-weight:bold"><?php echo stripslashes($_SESSION['admin_user']); ?></span>
Password: <span style="color:red; font-weight:bold"><?php echo stripslashes($_SESSION['admin_pass']); ?></span>
</pre>

    <br />&nbsp;

    </li>

    <table>
        <tr>
            <td>
                <form action="<?php echo HESK_PATH . $hesk_settings['admin_dir']; ?>/index.php" method="post">
                <input type="hidden" name="a" value="do_login" />
                <input type="hidden" name="remember_user" value="JUSTUSER" />
                <input type="hidden" name="user" value="<?php echo stripslashes($_SESSION['admin_user']); ?>" />
                <input type="hidden" name="pass" value="<?php echo stripslashes($_SESSION['admin_pass']); ?>" />
                <input type="hidden" name="goto" value="mail.php?a=read&amp;id=1" />
                <input type="submit" value="Read HESK quick start guide" class="orangebutton" onmouseover="hesk_btn(this,'orangebuttonover');" onmouseout="hesk_btn(this,'orangebutton');" />
                </form>
            </td>
            <td> - or -</td>
            <td>
                <form action="<?php echo HESK_PATH . $hesk_settings['admin_dir']; ?>/index.php" method="post">
                <input type="hidden" name="a" value="do_login" />
                <input type="hidden" name="remember_user" value="JUSTUSER" />
                <input type="hidden" name="user" value="<?php echo stripslashes($_SESSION['admin_user']); ?>" />
                <input type="hidden" name="pass" value="<?php echo stripslashes($_SESSION['admin_pass']); ?>" />
                <input type="hidden" name="goto" value="admin_settings_general.php" />
                <input type="submit" value="Skip directly to settings" class="orangebuttonsec" onmouseover="hesk_btn(this,'orangebuttonsecover');" onmouseout="hesk_btn(this,'orangebuttonsec');" />
                </form>
            </td>
        </tr>
    </table>

    </ol>

    <p>&nbsp;</p>

	<?php
    hesk_iFooter();
} // End hesk_iFinish()


function hesk_iTables()
{
	global $hesk_db_link, $hesk_settings;

// -> Attachments
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` (
  `att_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `ticket_id` varchar(13) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `saved_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `real_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `size` int(10) unsigned NOT NULL DEFAULT '0',
  `type` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  PRIMARY KEY (`att_id`),
  KEY `ticket_id` (`ticket_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Temporary Attachments (Drag & Drop)
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments` (
    `att_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `unique_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
    `saved_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
    `real_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
    `size` int(10) unsigned NOT NULL DEFAULT '0',
    `expires_at` timestamp NOT NULL,
    PRIMARY KEY (`att_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments_limits` (
    `ip` varchar(45) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
    `upload_count` int(10) unsigned NOT NULL DEFAULT 1,
    `last_upload_at` timestamp NOT NULL DEFAULT NOW(),
    PRIMARY KEY (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");

// -> Authentication tokens
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `selector` char(12) DEFAULT NULL,
  `token` char(64) DEFAULT NULL,
  `user_id` smallint(5) UNSIGNED NOT NULL,
  `user_type` varchar(8) NOT NULL DEFAULT 'STAFF',
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `expires` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");

// -> Banned emails
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."banned_emails` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL,
  `banned_by` smallint(5) unsigned NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `email` (`email`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8
");

// -> Banned IPs
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."banned_ips` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `ip_from` int(10) unsigned NOT NULL DEFAULT '0',
  `ip_to` int(10) unsigned NOT NULL DEFAULT '0',
  `ip_display` varchar(100) NOT NULL,
  `banned_by` smallint(5) unsigned NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8
");

// -> Bookmarks
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."bookmarks` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` smallint(5) unsigned NOT NULL,
  `ticket_id` mediumint(8) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `ticket_id` (`ticket_id`,`user_id`),
  KEY `user_id` (`user_id`,`ticket_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");

// -> Categories
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `cat_order` smallint(5) unsigned NOT NULL DEFAULT '0',
  `autoassign` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `autoassign_config` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL,
  `type` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `priority` tinyint(3) UNSIGNED NOT NULL DEFAULT '3',
  `default_due_date_amount` INT DEFAULT NULL,
  `default_due_date_unit` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `type` (`type`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// ---> Insert default category
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` (`id`, `name`, `cat_order`) VALUES (1, 'General', 10)");

// -> Custom fields
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` (
  `id` tinyint(3) UNSIGNED NOT NULL,
  `use` enum('0','1','2') NOT NULL DEFAULT '0',
  `place` enum('0','1') NOT NULL DEFAULT '0',
  `type` varchar(20) NOT NULL DEFAULT 'text',
  `req` enum('0','1','2') NOT NULL DEFAULT '0',
  `category` text,
  `name` text,
  `value` text,
  `order` smallint(5) UNSIGNED NOT NULL DEFAULT '10',
  PRIMARY KEY (`id`),
  KEY `useType` (`use`,`type`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// ---> Insert empty custom fields
hesk_dbQuery("
INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` (`id`, `use`, `place`, `type`, `req`, `category`, `name`, `value`, `order`) VALUES
(1, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(2, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(3, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(4, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(5, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(6, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(7, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(8, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(9, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(10, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(11, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(12, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(13, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(14, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(15, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(16, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(17, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(18, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(19, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(20, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(21, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(22, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(23, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(24, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(25, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(26, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(27, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(28, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(29, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(30, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(31, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(32, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(33, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(34, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(35, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(36, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(37, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(38, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(39, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(40, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(41, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(42, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(43, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(44, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(45, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(46, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(47, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(48, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(49, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(50, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(51, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(52, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(53, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(54, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(55, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(56, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(57, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(58, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(59, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(60, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(61, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(62, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(63, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(64, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(65, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(66, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(67, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(68, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(69, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(70, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(71, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(72, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(73, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(74, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(75, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(76, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(77, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(78, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(79, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(80, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(81, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(82, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(83, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(84, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(85, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(86, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(87, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(88, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(89, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(90, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(91, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(92, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(93, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(94, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(95, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(96, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(97, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(98, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(99, '0', '0', 'text', '0', NULL, '', NULL, 1000),
(100, '0', '0', 'text', '0', NULL, '', NULL, 1000)
");

// -> Custom Priorities
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` (
  `id` tinyint(3) UNSIGNED NOT NULL,
  `name` text NOT NULL,
  `color` varchar(6) NOT NULL,
  `can_customers_select` enum('0','1') NOT NULL DEFAULT '1',
  `priority_order` smallint(5) UNSIGNED NOT NULL DEFAULT '10',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

hesk_dbQuery("
INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` (`id`, `name`, `color`, `can_customers_select`, `priority_order`) VALUES
(0, '{\"English\":\"NULL\"}', 'e74441', '0', 4),
(1, '{\"English\":\"NULL\"}', 'fac500', '1', 3),
(2, '{\"English\":\"NULL\"}', '3abb7a', '1', 2),
(3, '{\"English\":\"NULL\"}', '71a5ec', '1', 1)
");

// -> Custom statuses
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_statuses` (
  `id` tinyint(3) UNSIGNED NOT NULL,
  `name` text NOT NULL,
  `color` varchar(6) NOT NULL,
  `can_customers_change` enum('0','1') NOT NULL DEFAULT '1',
  `order` smallint(5) UNSIGNED NOT NULL DEFAULT '10',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Customer Accounts
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `pass` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `language` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `verified` smallint(1) UNSIGNED NOT NULL DEFAULT 0,
  `verification_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `verification_email_sent_at` timestamp NULL,
  `mfa_enrollment` smallint(5) UNSIGNED NOT NULL DEFAULT 0,
  `mfa_secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `email` (`email`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Pending Customer Email Changes
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `customer_id` mediumint(8) unsigned NOT NULL,
  `new_email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `verification_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `expires_at` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
  PRIMARY KEY (`id`),
  KEY `email` (`new_email`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// --> Ticket to Customer Mapping
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `ticket_id` mediumint(8) unsigned NOT NULL,
  `customer_id` mediumint(8) unsigned NOT NULL,
  `customer_type` varchar(9) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'REQUESTER',
  PRIMARY KEY (`id`),
  KEY `ticket_id` (`ticket_id`),
  KEY `customer_id` (`customer_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Email ID to ticket mapping
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `email_id` varchar(1000) NOT NULL,
  `ticket_id` mediumint UNSIGNED NOT NULL,
  `reply_id` mediumint UNSIGNED DEFAULT NULL,
  `from_hesk` tinyint UNSIGNED NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `email_id` (`email_id`(100))
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> KB Articles
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `catid` smallint(5) unsigned NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `author` smallint(5) unsigned NOT NULL,
  `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `content` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `keywords` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `rating` float NOT NULL DEFAULT '0',
  `votes` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `views` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `type` enum('0','1','2') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `html` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `sticky` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `art_order` smallint(5) unsigned NOT NULL DEFAULT '0',
  `history` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `attachments` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `catid` (`catid`),
  KEY `sticky` (`sticky`),
  KEY `type` (`type`),
  FULLTEXT KEY `subject` (`subject`,`content`,`keywords`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> KB Attachments
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_attachments` (
  `att_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `saved_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `real_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `size` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`att_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> KB Categories
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `parent` smallint(5) unsigned NOT NULL,
  `articles` smallint(5) unsigned NOT NULL DEFAULT '0',
  `articles_private` smallint(5) unsigned NOT NULL DEFAULT '0',
  `articles_draft` smallint(5) unsigned NOT NULL DEFAULT '0',
  `cat_order` smallint(5) unsigned NOT NULL,
  `type` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `type` (`type`),
  KEY `parent` (`parent`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// ---> Insert default KB category
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` (`id`, `name`, `parent`, `cat_order`, `type`) VALUES (1, 'Knowledgebase', 0, 10, '0')");

// -> Linked Tickets Table
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."linked_tickets` (
  `id` mediumint(8) NOT NULL AUTO_INCREMENT,
  `ticket_id1` mediumint(8) NOT NULL,
  `ticket_id2` mediumint(8) NOT NULL,
  `dt_created` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `ticket_id1` (`ticket_id1`),
  KEY `ticket_id2` (`ticket_id2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Login attempts
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `ip` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
  `number` tinyint(3) unsigned NOT NULL DEFAULT '1',
  `last_attempt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ip` (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Log of overdue tickets
hesk_dbQuery("
CREATE TABLE IF NOT EXISTS `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ticket` mediumint(8) UNSIGNED NOT NULL,
  `category` smallint(5) UNSIGNED NOT NULL,
  `priority` tinyint(3) UNSIGNED NOT NULL,
  `status` tinyint(3) UNSIGNED NOT NULL,
  `owner` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
  `due_date` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
  `comments` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ticket` (`ticket`),
  KEY `category` (`category`),
  KEY `priority` (`priority`),
  KEY `status` (`status`),
  KEY `owner` (`owner`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");

// -> Private messages
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `from` smallint(5) unsigned NOT NULL,
  `to` smallint(5) unsigned NOT NULL,
  `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `read` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `deletedby` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `from` (`from`),
  KEY `to` (`to`,`read`,`deletedby`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// ---> Insert welcome email
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` (`id`, `from`, `to`, `subject`, `message`, `dt`, `read`, `deletedby`) VALUES (NULL, 9999, 1, 'Hesk quick start guide', '".hesk_dbEscape("</p><div style=\"text-align:justify; padding-left: 10px; padding-right: 10px;\">\r\n\r\n<h2 style=\"padding-left:0px\">Welcome to Hesk, an excellent tool for improving your customer support!</h2>\r\n\r\n<h3>Below is a short guide to help you get started.</h3>\r\n\r\n<div class=\"main__content notice-flash \">\r\n<div class=\"notification orange\">\r\nAn up-to-date and expanded guide is available at <a href=\"https://www.hesk.com/knowledgebase/?article=109\" target=\"_blank\">Hesk online Quick Start Guide</a>.</div>\r\n</div>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #1: Set up your profile</h3>\r\n\r\n<ol>\r\n<li>go to <a href=\"profile.php\">Profile</a>,</li>\r\n<li>set your name and email address.</li>\r\n</ol>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #2: Configure Hesk</h3>\r\n\r\n<ol>\r\n<li>go to <a href=\"admin_settings_general.php\">Settings</a>,</li>\r\n<li>for a quick start, modify these settings on the \"General\" tab:<br><br>\r\n<b>Website title</b> - enter the title of your main website (not your help desk),<br>\r\n<b>Website URL</b> - enter the URL of your main website,<br>\r\n<b>Webmaster email</b> - enter an alternative email address people can contact in case your Hesk database is down<br>&nbsp;\r\n</li>\r\n<li>you can come back to the settings page later and explore all the options. To view details about a setting, click the [?]</li>\r\n</ol>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #3: Add support categories</h3>\r\n\r\n<p>Go to <a href=\"manage_categories.php\">Categories</a> to add support ticket categories.</p>\r\n<p>You cannot delete the default category, but you can rename it.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #4: Add your support team members</h3>\r\n\r\n<p>Go to <a href=\"manage_users.php\">Team</a> to create new support staff accounts.</p>\r\n<p>You can use two user types in Hesk:</p>\r\n<ul>\r\n<li><b>Administrators</b> who have full access to all Hesk features</li>\r\n<li><b>Staff</b> who you can restrict access to categories and features</li>\r\n</ul>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #5: Useful tools</h3>\r\n\r\n<p>You can do a lot in the <a href=\"banned_emails.php\">Tools</a> section, for example:</p>\r\n<ul>\r\n<li>create custom ticket statuses,</li>\r\n<li>add custom input fields to the &quot;Submit a ticket&quot; form,</li>\r\n<li>make public announcements (Service messages),</li>\r\n<li>modify email templates,</li>\r\n<li>ban disruptive customers,</li>\r\n<li>and more.</li>\r\n</ul>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #6: Create a Knowledgebase</h3>\r\n\r\n<p>A Knowledgebase is a collection of articles, guides, and answers to frequently asked questions, usually organized in multiple categories.</p>\r\n<p>A clear and comprehensive knowledgebase can drastically reduce the number of support tickets you receive, thereby saving you significant time and effort in the long run.</p>\r\n<p>Go to <a href=\"manage_knowledgebase.php\">Knowledgebase</a> to create categories and write articles for your knowledgebase.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #7: Don't repeat yourself</h3>\r\n\r\n<p>Sometimes several support tickets address the same issues - allowing you to use pre-written (&quot;canned&quot;) responses.</p>\r\n<p>To compose canned responses, go to the <a href=\"manage_canned.php\">Templates &gt; Responses</a> page.</p>\r\n<p>Similarly, you can create <a href=\"manage_ticket_templates.php\">Templates &gt; Tickets</a> if your staff will be submitting support tickets on the client's behalf, for example, from telephone conversations.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #8: Secure your help desk</h3>\r\n\r\n<p>Make sure your help desk is as secure as possible by going through the <a href=\"https://www.hesk.com/knowledgebase/?article=82\">Hesk security checklist</a>.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #9: Stay updated</h3>\r\n\r\n<p>Hesk regularly receives improvements and bug fixes; make sure you know about them!</p>\r\n<ul>\r\n<li>for fast notifications, <a href=\"https://x.com/HESKdotCOM\" rel=\"nofollow\">follow Hesk on <b>X</b></a></li>\r\n<li>for email notifications, subscribe to our low-volume, zero-spam <a href=\"https://www.hesk.com/newsletter.php\">newsletter</a></li>\r\n</ul>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #10: Look professional</h3>\r\n\r\n<p><a href=\"https://www.hesk.com/get/hesk3-license\">Remove &quot;Powered by&quot; links</a> to support Hesk development and make it look more professional.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Step #11: Too much hassle? Switch to Hesk Cloud for the ultimate experience</h3>\r\n\r\n<p>Experience the best of Hesk by moving your help desk into the Hesk Cloud:</p>\r\n<ul>\r\n<li>exclusive advanced modules,</li>\r\n<li>automated updates,</li>\r\n<li>free migration of your existing Hesk tickets and settings,</li>\r\n<li>we take care of maintenance, server setup and optimization, backups, and more!</li>\r\n</ul>\r\n\r\n<p>&nbsp;<br><a href=\"https://www.hesk.com/get/hesk3-cloud\" class=\"btn btn--blue-border\" style=\"text-decoration:none\">Click here to learn more about Hesk Cloud</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>Again, welcome to Hesk, and enjoy using it!</p>\r\n\r\n<p>Klemen Stirn<br>\r\nFounder<br>\r\n<a href=\"https://www.hesk.com\">https://www.hesk.com</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n</div><p>")."', NOW(), '0', 9999)");

// -> MFA Verification Tokens
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` smallint(5) UNSIGNED NOT NULL,
  `user_type` varchar(8) NOT NULL DEFAULT 'STAFF', 
  `verification_token` varchar(255) NOT NULL,
  `expires_at` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `verification_token` (`verification_token`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");

// -> MFA Backup Codes
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `user_id` smallint(5) unsigned NOT NULL,
  `user_type` varchar(8) NOT NULL DEFAULT 'STAFF',
  `code` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");

//-> Muted emails
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."muted_emails` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL,
  `type` tinyint UNSIGNED NOT NULL DEFAULT '1',
  `muted_by` smallint(5) unsigned NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `email` (`email`),
  KEY `type` (`type`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8
");

// -> Notes
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `ticket` mediumint(8) unsigned NOT NULL,
  `who` smallint(5) unsigned NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `attachments` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `ticketid` (`ticket`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> OAuth providers
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_providers` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `authorization_url` text NOT NULL,
  `token_url` text NOT NULL,
  `client_id` text NOT NULL,
  `client_secret` text NOT NULL,
  `scope` text NOT NULL,
  `no_val_ssl` tinyint(4) NOT NULL DEFAULT '0',
  `verified` smallint NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");

// -> OAuth tokens
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_tokens` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `provider_id` int(11) NOT NULL,
  `token_value` text DEFAULT NULL,
  `token_type` varchar(32) NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `expires` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");

// -> Online
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."online` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` smallint(5) unsigned NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `tmp` int(11) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`),
  KEY `dt` (`dt`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Permission groups
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_groups` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` mediumint(8) unsigned NOT NULL,
  `category_id` smallint(5) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `group_id_category_id` (`group_id`, `category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` mediumint(8) unsigned NOT NULL,
  `feature` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `group_id_feature` (`group_id`, `feature`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` mediumint(8) unsigned NOT NULL,
  `user_id` smallint(5) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `group_id_user_id` (`group_id`, `user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Pipe loops
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `hits` smallint(1) unsigned NOT NULL DEFAULT '0',
  `message_hash` char(32) COLLATE utf8_unicode_ci NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `email` (`email`,`hits`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Pipe rejections
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_rejections` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `email` (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Replies
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `replyto` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `message_html` mediumtext COLLATE utf8_unicode_ci DEFAULT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `attachments` mediumtext COLLATE utf8_unicode_ci,
  `staffid` smallint(5) unsigned NOT NULL DEFAULT '0',
  `customer_id` mediumint(5) unsigned NULL DEFAULT NULL,
  `rating` enum('1','5') COLLATE utf8_unicode_ci DEFAULT NULL,
  `read` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `replyto` (`replyto`),
  KEY `dt` (`dt`),
  KEY `staffid` (`staffid`),
  KEY `customer_id` (`customer_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Reply drafts
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `owner` smallint(5) unsigned NOT NULL,
  `ticket` mediumint(8) unsigned NOT NULL,
  `message` mediumtext CHARACTER SET utf8 NOT NULL,
  `message_html` mediumtext CHARACTER SET utf8 DEFAULT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `owner` (`owner`),
  KEY `ticket` (`ticket`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Reset password
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `user` smallint(5) unsigned NOT NULL,
  `hash` char(64) NOT NULL,
  `ip` varchar(45) NOT NULL,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `user_type` varchar(8) NOT NULL DEFAULT 'STAFF',
  PRIMARY KEY (`id`),
  KEY `user` (`user`),
  KEY `hash_user_type` (`hash`, `user_type`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
");

// -> Service messages
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."service_messages` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `author` smallint(5) unsigned NOT NULL,
  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `language` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `style` enum('0','1','2','3','4') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `type` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `order` smallint(5) unsigned NOT NULL DEFAULT '0',
  `location` varchar(255) NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `type` (`type`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");

// -> Canned Responses
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."std_replies` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `message_html` mediumtext COLLATE utf8_unicode_ci DEFAULT NULL,
  `reply_order` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Tickets
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `trackid` varchar(13) COLLATE utf8_unicode_ci NOT NULL,
  `u_name` varchar(255) NOT NULL default '',
  `u_email` varchar(1000) NOT NULL default '',
  `category` smallint(5) unsigned NOT NULL DEFAULT '1',
  `priority` tinyint(3) UNSIGNED NOT NULL DEFAULT '3',
  `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `message_html` mediumtext COLLATE utf8_unicode_ci DEFAULT NULL,
  `dt` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
  `lastchange` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `firstreply` timestamp NULL DEFAULT NULL,
  `closedat` timestamp NULL DEFAULT NULL,
  `articles` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `ip` varchar(45) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `language` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `status` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `openedby` mediumint(8) DEFAULT '0',
  `firstreplyby` smallint(5) unsigned DEFAULT NULL,
  `closedby` mediumint(8) NULL DEFAULT NULL,
  `replies` smallint(5) unsigned NOT NULL DEFAULT '0',
  `staffreplies` smallint(5) unsigned NOT NULL DEFAULT '0',
  `owner` smallint(5) unsigned NOT NULL DEFAULT '0',
  `assignedby` mediumint(8) NULL DEFAULT NULL,
  `time_worked` time NOT NULL DEFAULT '00:00:00',
  `lastreplier` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `replierid` smallint(5) unsigned DEFAULT NULL,
  `archive` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `locked` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `attachments` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `merged` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `history` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom1` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom2` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom3` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom4` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom5` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom6` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom7` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom8` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom9` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom10` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom11` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom12` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom13` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom14` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom15` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom16` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom17` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom18` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom19` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom20` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom21` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom22` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom23` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom24` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom25` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom26` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom27` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom28` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom29` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom30` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom31` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom32` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom33` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom34` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom35` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom36` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom37` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom38` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom39` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom40` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom41` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom42` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom43` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom44` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom45` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom46` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom47` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom48` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom49` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom50` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom51` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom52` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom53` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom54` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom55` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom56` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom57` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom58` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom59` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom60` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom61` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom62` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom63` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom64` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom65` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom66` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom67` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom68` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom69` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom70` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom71` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom72` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom73` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom74` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom75` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom76` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom77` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom78` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom79` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom80` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom81` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom82` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom83` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom84` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom85` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom86` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom87` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom88` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom89` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom90` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom91` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom92` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom93` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom94` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom95` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom96` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom97` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom98` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom99` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `custom100` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `due_date` timestamp NULL DEFAULT NULL,
  `overdue_email_sent` tinyint(1) DEFAULT '0',
  `satisfaction_email_sent` tinyint(1) DEFAULT '0',
  `satisfaction_email_dt` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `trackid` (`trackid`),
  KEY `archive` (`archive`),
  KEY `categories` (`category`),
  KEY `statuses` (`status`),
  KEY `owner` (`owner`),
  KEY `openedby` (`openedby`,`firstreplyby`,`closedby`),
  KEY `dt` (`dt`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// -> Ticket templates
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_templates` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
  `message_html` mediumtext COLLATE utf8_unicode_ci DEFAULT NULL,
  `tpl_order` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

// --> Ticket to Collaborator Mapping
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `ticket_id` mediumint UNSIGNED NOT NULL,
  `user_id` smallint UNSIGNED NOT NULL,
  PRIMARY KEY (`id`),
  KEY `ticket_id` (`ticket_id`),
  KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
");

// -> Users
hesk_dbQuery("
CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `user` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `pass` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `isadmin` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `active` smallint NOT NULL DEFAULT 1,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `nickname` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `signature` varchar(1000) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `language` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `categories` varchar(500) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `afterreply` enum('0','1','2') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `autostart` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `autoreload` smallint(5) unsigned NOT NULL DEFAULT '0',
  `notify_customer_new` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_customer_reply` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `show_suggested` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_new_unassigned` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_new_my` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_reply_unassigned` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_reply_my` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_assigned` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_pm` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_note` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_overdue_unassigned` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_overdue_my` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_customer_approval` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `notify_collaborator_added` enum('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '1',
  `notify_collaborator_customer_reply` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_collaborator_staff_reply` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `notify_collaborator_note` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `notify_collaborator_resolved` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `notify_collaborator_overdue` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `default_list` varchar(1000) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `autoassign` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
  `heskprivileges` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL,
  `ratingneg` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `ratingpos` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `rating` float NOT NULL DEFAULT '0',
  `replies` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `mfa_enrollment` smallint(5) UNSIGNED NOT NULL DEFAULT 0,
  `mfa_secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `autoassign` (`autoassign`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
");

hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."users` (`id`, `user`, `pass`, `isadmin`, `name`, `email`, `heskprivileges`) VALUES (1, '".hesk_dbEscape($_SESSION['admin_user'])."', '".hesk_dbEscape($_SESSION['admin_hash'])."', '1', '".hesk_dbEscape($_SESSION['admin_name'])."', '".hesk_dbEscape($_SESSION['admin_email'])."', '')");

return true;

} // End hesk_iTables()


function hesk_iSaveSettings()
{
	global $hesk_settings, $hesklang;

	$spam_question = hesk_generate_SPAM_question();

	$hesk_settings['secimg_use'] = empty($_SESSION['set_captcha']) ? 0 : 1;
	$hesk_settings['use_spamq'] = empty($_SESSION['use_spamq']) ? 0 : 1;
	$hesk_settings['question_ask'] = $spam_question[0];
	$hesk_settings['question_ans'] = $spam_question[1];
	$hesk_settings['set_attachments'] = empty($_SESSION['set_attachments']) ? 0 : 1;
	$hesk_settings['hesk_version'] = HESK_NEW_VERSION;

	if (isset($_SERVER['HTTP_HOST']))
	{
        $protocol = HESK_SSL? 'https://' : 'http://';
		$hesk_settings['site_url']=$protocol . $_SERVER['HTTP_HOST'];

		if (isset($_SERVER['REQUEST_URI']))
		{
			$hesk_settings['hesk_url']=$protocol . $_SERVER['HTTP_HOST'] . str_replace('/install/install.php','',$_SERVER['REQUEST_URI']);
		}
	}

	/* Encode and escape characters */
	$set = $hesk_settings;
	foreach ($hesk_settings as $k=> $v)
	{
		if (is_array($v) || is_object($v))
		{
			continue;
		}
		$set[$k] = addslashes($v);
	}
	$set['debug_mode'] = 0;

	$set['email_providers'] = count($set['email_providers']) ?  "'" . implode("','", $set['email_providers']) . "'" : '';
	$set['notify_spam_tags'] = count($set['notify_spam_tags']) ?  "'" . implode("','", $set['notify_spam_tags']) . "'" : '';

    // If SSL is enabled, let's force it by default
    $set['force_ssl'] = HESK_SSL ? 1 : 0;

    // Generate an URL access key
    $length = mt_rand(20, 30);
    $result = '';
    $characters = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ1234567890-_.';
    $charactersLength = strlen($characters);
    for ($i = 0; $i < $length; $i++) {
        $result .=  $characters[mt_rand(0, $charactersLength-1)];
    }
    $set['url_key'] = $result;

	hesk_iSaveSettingsFile($set);

	return true;
} // End hesk_iSaveSettings()
?>
install_functions.inc.php
wget 'https://lists2.roe3.org/hesk/_install/install_functions.inc.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// We will be installing this HESK version:
define('HESK_NEW_VERSION','3.7.10');
define('REQUIRE_PHP_VERSION','5.6.0');
define('REQUIRE_MYSQL_VERSION','5.0.7');

// Other required files and settings
define('INSTALL',1);
define('HIDE_ONLINE',1);

require(HESK_PATH . 'hesk_settings.inc.php');

$hesk_settings['debug_mode'] = 1;

// Save existing language values
if (isset($hesk_settings['language'])) {
    $hesk_settings['language_backup'] = $hesk_settings['language'];
} else {
    $hesk_settings['language_backup'] = 'English';
}

if (isset($hesk_settings['languages'])) {
    $hesk_settings['languages_backup'] = $hesk_settings['languages'];
} else {
    $hesk_settings['languages_backup'] = array('English' => array('folder'=>'en','hr'=>'------ Reply above this line ------'));
}

// Set default just in case we are upgrading from a very old version
$hesk_settings['language'] = 'English';
$hesk_settings['languages'] = array('English' => array('folder'=>'en','hr'=>'------ Reply above this line ------'));

if (!isset($hesk_settings['x_frame_opt']))
{
    $hesk_settings['x_frame_opt'] = 1;
}

if (!isset($hesk_settings['samesite']))
{
    $hesk_settings['samesite'] = 'Lax';
}

if (!isset($hesk_settings['force_ssl']))
{
    $hesk_settings['force_ssl'] = 0;
}
// Added for IMAP Mailbox
if (!isset($hesk_settings['imap_mailbox']))
{
    $hesk_settings['imap_mailbox'] = 'INBOX';
}

//Added for direct attachment settings
if (!isset($hesk_settings['attachments']['attachment_in_email_type']))
{
    $hesk_settings['attachments']['attachment_in_email_type'] = 0;
    $hesk_settings['attachments']['direct_attachment_in_email'] = 0;
    $hesk_settings['attachments']['direct_attachment_in_email_no_of_files'] = 2;
    $hesk_settings['attachments']['first_x_attachments'] = 2;
    $hesk_settings['attachments']['file_max_size'] = 2048;
}

error_reporting(E_ALL);

// Database upgrades from old versions can take quite some time, remove time limit
if ( function_exists('set_time_limit') )
{
	set_time_limit(0);
}

require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/admin_functions.inc.php');
require(HESK_PATH . 'inc/setup_functions.inc.php');
hesk_load_database_functions();

// Start the session
hesk_session_start();


// ******* FUNCTIONS ******* //


function hesk_iTestDatabaseConnection($use_existing_settings = false)
{
	global $hesk_db_link, $hesk_settings, $hesklang;

    $db_success = 1;

	// Get MySQL settings, except for successful updates
	if ( ! $use_existing_settings)
	{
		$hesk_settings['db_host'] = hesk_input( hesk_POST('host') );
		$hesk_settings['db_name'] = hesk_input( hesk_POST('name') );
		$hesk_settings['db_user'] = str_replace('&amp;', '&', hesk_input( hesk_POST('user') ) );
		$hesk_settings['db_pass'] = str_replace(array('&amp;', '&gt;', '&lt;'), array('&', '>', '<'), hesk_input( hesk_POST('pass') ) );

		if (INSTALL_PAGE == 'install.php')
		{
			// Get table prefix, don't allow any special chars
			$hesk_settings['db_pfix'] = preg_replace('/[^0-9a-zA-Z_]/', '', hesk_POST('pfix', 'hesk_') );
		}
	}

	// Use MySQLi extension to connect?
	$use_mysqli = function_exists('mysqli_connect') ? true : false;

    // Start output buffering
    ob_start();

    // Connect to database
    if ($use_mysqli)
    {
        mysqli_report(MYSQLI_REPORT_OFF);

		// Do we need a special port? Check and connect to the database
		if ( strpos($hesk_settings['db_host'], ':') )
		{
			list($hesk_settings['db_host_no_port'], $hesk_settings['db_port']) = explode(':', $hesk_settings['db_host']);
            try {
                $hesk_db_link = mysqli_connect($hesk_settings['db_host_no_port'], $hesk_settings['db_user'], $hesk_settings['db_pass'], $hesk_settings['db_name'], intval($hesk_settings['db_port']) );
            } catch (Exception $e) {
                $db_success=0;
            }
		}
		else
		{
            try {
                $hesk_db_link = mysqli_connect($hesk_settings['db_host'], $hesk_settings['db_user'], $hesk_settings['db_pass'], $hesk_settings['db_name']);
            } catch (Exception $e) {
                $db_success=0;
            }
		}
    }
    else
    {
    	$hesk_db_link = mysql_connect($hesk_settings['db_host'],$hesk_settings['db_user'], $hesk_settings['db_pass']) or $db_success=0;

        // Select database works OK?
        if ($db_success == 1 && ! mysql_select_db($hesk_settings['db_name'], $hesk_db_link) )
        {
	    	// No, try to create the database
			if (function_exists('mysql_create_db') && mysql_create_db($hesk_settings['db_name'], $hesk_db_link))
	        {
	        	if (mysql_select_db($hesk_settings['db_name'], $hesk_db_link))
	            {
					$db_success = 1;
	            }
	            else
	            {
					$db_success = 0;
	            }
	        }
	        else
	        {
	        	$db_success = 0;
	        }
        }
    }

	ob_end_clean();

    if (!isset($hesk_db_link) || $hesk_db_link === false) {
        $db_success=0;
    }

	// Test DB permissions
	if ($db_success)
	{
		$sql[0] = "DROP TABLE IF EXISTS `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test`";
		$sql[1] = "CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test` (`id` smallint(1) unsigned NOT NULL DEFAULT '0') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
		$sql[2] = "INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test` (`id`) VALUES ('0')";
		$sql[3] = "SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test` WHERE `id`='0' LIMIT 1";
		$sql[4] = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test` SET `id`='1' WHERE `id`='0'";
		$sql[5] = "DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test` WHERE `id`='0'";
		$sql[6] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test` ADD `name` CHAR(1) NULL DEFAULT NULL AFTER `id`";
		$sql[7] = "CREATE INDEX `name` ON `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test`(`name`) ";
		$sql[8] = "DROP TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mysql_test`";

		if ($use_mysqli)
		{
			for ($i=0; $i<=8; $i++)
			{
				if (! $res = mysqli_query($hesk_db_link, $sql[$i]))
				{
					global $mysql_log;
					$mysql_log = mysqli_error($hesk_db_link);
					hesk_iDatabase(1);
				}
			}
		}
		else
		{
			for ($i=0; $i<=8; $i++)
			{
				if (! $res = mysql_query($sql[$i], $hesk_db_link))
				{
					global $mysql_log;
					$mysql_log = mysql_error();
					hesk_iDatabase(1);
				}
			}
		}
	}

    // Any errors?
	if ( ! $db_success)
    {
    	global $mysql_log;
    	$mysql_log = $use_mysqli ? mysqli_connect_error() : mysql_error();

		hesk_iDatabase(1);
    }

	// Check MySQL version
	define('MYSQL_VERSION', hesk_dbResult( hesk_dbQuery('SELECT VERSION() AS version') ) );
	if ( version_compare(MYSQL_VERSION,REQUIRE_MYSQL_VERSION,'<') )
	{
		hesk_iDatabase(5);
	}

    // We need utf-8
    hesk_dbSetNames();

	return $hesk_db_link;

} // END hesk_iTestDatabaseConnection()


function hesk_iSaveSettingsFile($set)
{
	global $hesk_settings, $hesklang;

	// Make sure OPcache is reset when modifying settings
	if ( function_exists('opcache_reset') )
	{
		opcache_reset();
	}

    // This associative array requires special attention to be saved correctly
    $set['theme_overrides_string'] = '';
    foreach ($set['theme_overrides'] as $k => $v) {
        $set['theme_overrides_string'] .= "'$k' => '$v', ";
    }
    $set['theme_overrides_string'] = rtrim($set['theme_overrides_string'], ', ');

	$settings_file_content='<?php
// Settings file for HESK ' . $set['hesk_version'] . '

// ==> GENERAL

// --> General settings
$hesk_settings[\'site_title\']=\'' . $set['site_title'] . '\';
$hesk_settings[\'site_url\']=\'' . $set['site_url'] . '\';
$hesk_settings[\'hesk_title\']=\'' . $set['hesk_title'] . '\';
$hesk_settings[\'hesk_url\']=\'' . $set['hesk_url'] . '\';
$hesk_settings[\'webmaster_mail\']=\'' . $set['webmaster_mail'] . '\';
$hesk_settings[\'site_theme\']=\'' . $set['site_theme'] . '\';
$hesk_settings[\'admin_css\']=' . $set['admin_css'] . ';
$hesk_settings[\'admin_css_url\']=\'' . $set['admin_css_url'] . '\';
$hesk_settings[\'admin_js\']=' . $set['admin_js'] . ';
$hesk_settings[\'admin_js_url\']=\'' . $set['admin_js_url'] . '\';

// --> Language settings
$hesk_settings[\'can_sel_lang\']=' . $set['can_sel_lang'] . ';
$hesk_settings[\'language\']=\'' . $set['language'] . '\';
$hesk_settings[\'languages\']=array(
'.hesk_getLanguageForFile($set, 'languages').');

// --> Database settings
$hesk_settings[\'db_host\']=\'' . $set['db_host'] . '\';
$hesk_settings[\'db_name\']=\'' . $set['db_name'] . '\';
$hesk_settings[\'db_user\']=\'' . $set['db_user'] . '\';
$hesk_settings[\'db_pass\']=\'' . $set['db_pass'] . '\';
$hesk_settings[\'db_pfix\']=\'' . $set['db_pfix'] . '\';


// ==> HELP DESK

// --> Help desk settings
$hesk_settings[\'admin_dir\']=\'' . $set['admin_dir'] . '\';
$hesk_settings[\'attach_dir\']=\'' . $set['attach_dir'] . '\';
$hesk_settings[\'cache_dir\']=\'' . $set['cache_dir'] . '\';
$hesk_settings[\'max_listings\']=' . $set['max_listings'] . ';
$hesk_settings[\'print_font_size\']=' . $set['print_font_size'] . ';
$hesk_settings[\'autoclose\']=' . $set['autoclose'] . ';
$hesk_settings[\'max_open\']=' . $set['max_open'] . ';
$hesk_settings[\'due_soon\']=' . $set['due_soon'] . ';
$hesk_settings[\'new_top\']=' . $set['new_top'] . ';
$hesk_settings[\'reply_top\']=' . $set['reply_top'] . ';
$hesk_settings[\'hide_replies\']=' . $set['hide_replies'] . ';
$hesk_settings[\'limit_width\']=' . $set['limit_width'] . ';

// --> Features
$hesk_settings[\'autologin\']=' . $set['autologin'] . ';
$hesk_settings[\'autoassign\']=' . $set['autoassign'] . ';
$hesk_settings[\'require_email\']=' . $set['require_email'] . ';
$hesk_settings[\'require_owner\']=' . $set['require_owner'] . ';
$hesk_settings[\'require_subject\']=' . $set['require_subject'] . ';
$hesk_settings[\'require_message\']=' . $set['require_message'] . ';
$hesk_settings[\'custclose\']=' . $set['custclose'] . ';
$hesk_settings[\'custopen\']=' . $set['custopen'] . ';
$hesk_settings[\'rating\']=' . $set['rating'] . ';
$hesk_settings[\'cust_urgency\']=' . $set['cust_urgency'] . ';
$hesk_settings[\'sequential\']=' . $set['sequential'] . ';
$hesk_settings[\'time_worked\']=' . $set['time_worked'] . ';
$hesk_settings[\'spam_notice\']=' . $set['spam_notice'] . ';
$hesk_settings[\'list_users\']=' . $set['list_users'] . ';
$hesk_settings[\'debug_mode\']=' . $set['debug_mode'] . ';
$hesk_settings[\'short_link\']=' . $set['short_link'] . ';
$hesk_settings[\'submitting_wait\']=' . $set['submitting_wait'] . ';
$hesk_settings[\'remember_custom_field_values\']=' . $set['remember_custom_field_values'] . ';
$hesk_settings[\'disable_autofill_admin\']=' . $set['disable_autofill_admin'] . ';
$hesk_settings[\'disable_autofill_customer\']=' . $set['disable_autofill_customer'] . ';
$hesk_settings[\'extend_admin\']=' . $set['extend_admin'] . ';
$hesk_settings[\'extend_customer\']=' . $set['extend_customer'] . ';
$hesk_settings[\'select_cat\']=' . $set['select_cat'] . ';
$hesk_settings[\'select_pri\']=' . $set['select_pri'] . ';
$hesk_settings[\'cat_show_select\']=' . $set['cat_show_select'] . ';
$hesk_settings[\'staff_ticket_formatting\']=' . $set['staff_ticket_formatting'] . ';
$hesk_settings[\'staff_nicknames\']=' . $set['staff_nicknames'] . ';

// --> Barcode
$hesk_settings[\'barcode\']=array(
\'print\' => ' . $set['barcode']['print'] . ',
\'staff_only\' => ' . $set['barcode']['staff_only'] . ',
\'type\' => \'' . $set['barcode']['type'] . '\',
\'format\' => \'' . $set['barcode']['format'] . '\',
\'width\' => ' . $set['barcode']['width'] . ',
\'height\' => ' . $set['barcode']['height'] . ',
\'color\' => \'' . $set['barcode']['color'] . '\',
\'bg\' => \'' . $set['barcode']['bg'] . '\',
);

// --> Customer Accounts
$hesk_settings[\'customer_accounts\']=' . $set['customer_accounts'] . ';
$hesk_settings[\'customer_accounts_required\']=' . $set['customer_accounts_required'] . ';
$hesk_settings[\'customer_accounts_customer_self_register\']=' . $set['customer_accounts_customer_self_register'] . ';
$hesk_settings[\'customer_accounts_admin_approvals\']=' . $set['customer_accounts_admin_approvals'] . ';
$hesk_settings[\'customer_autologin\']=' . $set['customer_autologin'] . ';
$hesk_settings[\'customer_accounts_allow_email_changes\']=' . $set['customer_accounts_allow_email_changes'] . ';
$hesk_settings[\'customer_accounts_verify_email_cooldown\']=' . $set['customer_accounts_verify_email_cooldown'] . ';

// --> SPAM Prevention
$hesk_settings[\'secimg_use\']=' . $set['secimg_use'] . ';
$hesk_settings[\'secimg_sum\']=\'' . $set['secimg_sum'] . '\';
$hesk_settings[\'recaptcha_use\']=' . $set['recaptcha_use'] . ';
$hesk_settings[\'recaptcha_public_key\']=\'' . $set['recaptcha_public_key'] . '\';
$hesk_settings[\'recaptcha_private_key\']=\'' . $set['recaptcha_private_key'] . '\';
$hesk_settings[\'question_use\']=' . $set['question_use'] . ';
$hesk_settings[\'question_ask\']=\'' . $set['question_ask'] . '\';
$hesk_settings[\'question_ans\']=\'' . $set['question_ans'] . '\';

// --> Security
$hesk_settings[\'attempt_limit\']=' . $set['attempt_limit'] . ';
$hesk_settings[\'attempt_banmin\']=' . $set['attempt_banmin'] . ';
$hesk_settings[\'flood\']=' . $set['flood'] . ';
$hesk_settings[\'reset_pass\']=' . $set['reset_pass'] . ';
$hesk_settings[\'email_view_ticket\']=' . $set['email_view_ticket'] . ';
$hesk_settings[\'x_frame_opt\']=' . $set['x_frame_opt'] . ';
$hesk_settings[\'samesite\']=\'' . $set['samesite'] . '\';
$hesk_settings[\'force_ssl\']=' . $set['force_ssl'] . ';
$hesk_settings[\'url_key\']=\'' . $set['url_key'] . '\';
$hesk_settings[\'require_mfa\']=' . $set['require_mfa'] . ';
$hesk_settings[\'require_mfa_customers\']=' . $set['require_mfa_customers'] . ';
$hesk_settings[\'elevator_duration\']=\'' . $set['elevator_duration'] . '\';

// --> Attachments
$hesk_settings[\'attachments\']=array(
\'use\' => ' . $set['attachments']['use'] . ',
\'max_number\' => ' . $set['attachments']['max_number'] . ',
\'max_size\' => ' . $set['attachments']['max_size'] . ',
\'allowed_types\' => array(\'' . implode('\',\'',$set['attachments']['allowed_types']) . '\'),
\'attachment_in_email_type\' => ' .$set['attachments']['attachment_in_email_type'].',
\'direct_attachment_in_email\' => ' .$set['attachments']['direct_attachment_in_email'].',
\'direct_attachment_in_email_no_of_files\' => ' .$set['attachments']['direct_attachment_in_email_no_of_files'].',
\'first_x_attachments\' => ' .$set['attachments']['first_x_attachments'].',
\'file_max_size\' => ' .$set['attachments']['file_max_size'].',
);


// ==> KNOWLEDGEBASE

// --> Knowledgebase settings
$hesk_settings[\'kb_enable\']=' . $set['kb_enable'] . ';
$hesk_settings[\'kb_wysiwyg\']=' . $set['kb_wysiwyg'] . ';
$hesk_settings[\'kb_search\']=' . $set['kb_search'] . ';
$hesk_settings[\'kb_search_limit\']=' . $set['kb_search_limit'] . ';
$hesk_settings[\'kb_views\']=' . $set['kb_views'] . ';
$hesk_settings[\'kb_date\']=' . $set['kb_date'] . ';
$hesk_settings[\'kb_recommendanswers\']=' . $set['kb_recommendanswers'] . ';
$hesk_settings[\'kb_rating\']=' . $set['kb_rating'] . ';
$hesk_settings[\'kb_substrart\']=' . $set['kb_substrart'] . ';
$hesk_settings[\'kb_cols\']=' . $set['kb_cols'] . ';
$hesk_settings[\'kb_numshow\']=' . $set['kb_numshow'] . ';
$hesk_settings[\'kb_popart\']=' . $set['kb_popart'] . ';
$hesk_settings[\'kb_latest\']=' . $set['kb_latest'] . ';
$hesk_settings[\'kb_index_popart\']=' . $set['kb_index_popart'] . ';
$hesk_settings[\'kb_index_latest\']=' . $set['kb_index_latest'] . ';
$hesk_settings[\'kb_related\']=' . $set['kb_related'] . ';


// ==> EMAIL

// --> Email sending
$hesk_settings[\'noreply_mail\']=\'' . $set['noreply_mail'] . '\';
$hesk_settings[\'noreply_name\']=\'' . $set['noreply_name'] . '\';
$hesk_settings[\'email_max_recipients\']=' . $set['email_max_recipients'] . ';
$hesk_settings[\'email_formatting\']=' . $set['email_formatting'] . ';
$hesk_settings[\'smtp\']=' . $set['smtp'] . ';
$hesk_settings[\'smtp_host_name\']=\'' . $set['smtp_host_name'] . '\';
$hesk_settings[\'smtp_host_port\']=' . $set['smtp_host_port'] . ';
$hesk_settings[\'smtp_timeout\']=' . $set['smtp_timeout'] . ';
$hesk_settings[\'smtp_enc\']=\'' . $set['smtp_enc'] . '\';
$hesk_settings[\'smtp_noval_cert\']=' . $set['smtp_noval_cert'] . ';
$hesk_settings[\'smtp_user\']=\'' . $set['smtp_user'] . '\';
$hesk_settings[\'smtp_password\']=\'' . $set['smtp_password'] . '\';
$hesk_settings[\'smtp_conn_type\']=\'' . $set['smtp_conn_type'] . '\';
$hesk_settings[\'smtp_oauth_provider\']=' . $set['smtp_oauth_provider'] . ';

// --> Email piping
$hesk_settings[\'email_piping\']=' . $set['email_piping'] . ';

// --> IMAP Fetching
$hesk_settings[\'imap\']=' . $set['imap'] . ';
$hesk_settings[\'imap_job_wait\']=' . $set['imap_job_wait'] . ';
$hesk_settings[\'imap_host_name\']=\'' . $set['imap_host_name'] . '\';
$hesk_settings[\'imap_host_port\']=' . $set['imap_host_port'] . ';
$hesk_settings[\'imap_enc\']=\'' . $set['imap_enc'] . '\';
$hesk_settings[\'imap_noval_cert\']=' . $set['imap_noval_cert'] . ';
$hesk_settings[\'imap_disable_GSSAPI\']=' . $set['imap_disable_GSSAPI'] . ';
$hesk_settings[\'imap_keep\']=' . $set['imap_keep'] . ';
$hesk_settings[\'imap_user\']=\'' . $set['imap_user'] . '\';
$hesk_settings[\'imap_password\']=\'' . $set['imap_password'] . '\';
$hesk_settings[\'imap_conn_type\']=\'' . $set['imap_conn_type'] . '\';
$hesk_settings[\'imap_oauth_provider\']=' . $set['imap_oauth_provider'] . ';
$hesk_settings[\'imap_mailbox\']=\''. $set['imap_mailbox'] . '\';

// --> POP3 Fetching
$hesk_settings[\'pop3\']=' . $set['pop3'] . ';
$hesk_settings[\'pop3_job_wait\']=' . $set['pop3_job_wait'] . ';
$hesk_settings[\'pop3_host_name\']=\'' . $set['pop3_host_name'] . '\';
$hesk_settings[\'pop3_host_port\']=' . $set['pop3_host_port'] . ';
$hesk_settings[\'pop3_tls\']=' . $set['pop3_tls'] . ';
$hesk_settings[\'pop3_keep\']=' . $set['pop3_keep'] . ';
$hesk_settings[\'pop3_user\']=\'' . $set['pop3_user'] . '\';
$hesk_settings[\'pop3_password\']=\'' . $set['pop3_password'] . '\';
$hesk_settings[\'pop3_conn_type\']=\'' . $set['pop3_conn_type'] . '\';
$hesk_settings[\'pop3_oauth_provider\']=' . $set['pop3_oauth_provider'] . ';

$hesk_settings[\'strip_quoted\']=' . $set['strip_quoted'] . ';
$hesk_settings[\'eml_req_msg\']=' . $set['eml_req_msg'] . ';
$hesk_settings[\'save_embedded\']=' . $set['save_embedded'] . ';
$hesk_settings[\'email_include_to\']=' . $set['email_include_to'] . ';
$hesk_settings[\'email_include_cc\']=' . $set['email_include_cc'] . ';

// --> Ignore emails
$hesk_settings[\'pipe_block_noreply\']=' . $set['pipe_block_noreply'] . ';
$hesk_settings[\'pipe_block_returned\']=' . $set['pipe_block_returned'] . ';
$hesk_settings[\'pipe_block_duplicate\']=' . $set['pipe_block_duplicate'] . ';
$hesk_settings[\'loop_hits\']=' . $set['loop_hits'] . ';
$hesk_settings[\'loop_time\']=' . $set['loop_time'] . ';
$hesk_settings[\'pipe_customer_rejection_notification\']=' . $set['pipe_customer_rejection_notification'] . ';
$hesk_settings[\'pipe_customer_rejection_email_cooldown_hours\']=' . $set['pipe_customer_rejection_email_cooldown_hours'] . ';

// --> Detect email typos
$hesk_settings[\'detect_typos\']=' . $set['detect_typos'] . ';
$hesk_settings[\'email_providers\']=array(' . $set['email_providers'] . ');

// --> Notify customer when
$hesk_settings[\'notify_new\']=' . $set['notify_new'] . ';
$hesk_settings[\'notify_skip_spam\']=' . $set['notify_skip_spam'] . ';
$hesk_settings[\'notify_spam_tags\']=array(' . $set['notify_spam_tags'] . ');
$hesk_settings[\'notify_closed\']=' . $set['notify_closed'] . ';

// --> Other
$hesk_settings[\'multi_eml\']=' . $set['multi_eml'] . ';
$hesk_settings[\'confirm_email\']=' . $set['confirm_email'] . ';
$hesk_settings[\'open_only\']=' . $set['open_only'] . ';


// ==> TICKET LIST

$hesk_settings[\'ticket_list\']=array(\'' . implode('\',\'',$set['ticket_list']) . '\');
$hesk_settings[\'customer_ticket_list\']=array(\'' . implode('\',\'',$set['customer_ticket_list']) . '\');

// --> Other
$hesk_settings[\'submittedformat\']=' . $set['submittedformat'] . ';
$hesk_settings[\'updatedformat\']=' . $set['updatedformat'] . ';
$hesk_settings[\'format_submitted\']=\'' . $set['format_submitted'] . '\';
$hesk_settings[\'format_updated\']=\'' . $set['format_updated'] . '\';
$hesk_settings[\'email_column\']=' . $set['email_column'] . ';


// ==> MISC

// --> Date & Time
$hesk_settings[\'timezone\']=\'' . $set['timezone'] . '\';
$hesk_settings[\'format_time\']=\'' . $set['format_time'] . '\';
$hesk_settings[\'format_date\']=\'' . $set['format_date'] . '\';
$hesk_settings[\'format_timestamp\']=\'' . $set['format_timestamp'] . '\';
$hesk_settings[\'time_display\']=' . $set['time_display'] . ';
$hesk_settings[\'format_datepicker_js\']=\'' . $set['format_datepicker_js'] . '\';
$hesk_settings[\'format_datepicker_php\']=\'' . $set['format_datepicker_php'] . '\';

// --> Other
$hesk_settings[\'ip_whois\']=\'' . $set['ip_whois'] . '\';
$hesk_settings[\'maintenance_mode\']=' . $set['maintenance_mode'] . ';
$hesk_settings[\'alink\']=' . $set['alink'] . ';
$hesk_settings[\'submit_notice\']=' . $set['submit_notice'] . ';
$hesk_settings[\'online\']=' . $set['online'] . ';
$hesk_settings[\'online_min\']=' . $set['online_min'] . ';
$hesk_settings[\'check_updates\']=' . $set['check_updates'] . ';


// ==> LOOK & FEEL
$hesk_settings[\'customer_theme\']=\'' . $set['customer_theme'] . '\';
$hesk_settings[\'theme_overrides\']=array(' . $set['theme_overrides_string'] . ');


#############################
#     DO NOT EDIT BELOW     #
#############################
$hesk_settings[\'hesk_version\']=\'' . $set['hesk_version'] . '\';
if ($hesk_settings[\'debug_mode\'])
{
    error_reporting(E_ALL);
}
else
{
    error_reporting(0);
}
if (!defined(\'IN_SCRIPT\')) {die(\'Invalid attempt!\');}';

	// Write to the settings file
	if ( ! file_put_contents(HESK_PATH . 'hesk_settings.inc.php', $settings_file_content, LOCK_EX) )
	{
		hesk_error($hesklang['err_openset']);
	}

	return true;
} // END hesk_iSaveSettingsFile()


function hesk_iDatabase($problem=0)
{
    global $hesk_settings, $hesk_db_link, $mysql_log;

    hesk_iHeader();
	?>

	<h3>Database settings</h3>

	<br />

	<?php
	if ($problem == 1)
	{
	    hesk_show_error('<br /><br />Double-check all the information below. Contact your hosting company for the correct information to use!<br /><br /><b>MySQL said:</b> '.$mysql_log.'</p>', 'Database connection failed');
	}
    elseif ($problem == 2)
    {
	    hesk_show_error('<b>Database tables already exist!</b><br /><br />
        HESK database tables with <b>'.$hesk_settings['db_pfix'].'</b> prefix already exist in this database!<br /><br />
	    To upgrade an existing HESK installation select <a href="index.php">Update existing install</a> instead.<br /><br />
	    To install a new copy of HESK in the same database use a unique table prefix.');
    }
    elseif ($problem == 3)
    {
	    hesk_show_error('<b>Old database tables not found!</b><br /><br />
        HESK database tables have not been found in this database!<br /><br />
	    To install HESK use the <a href="index.php">New install</a> option instead.');
    }
    elseif ($problem == 4)
    {
	    hesk_show_error('<b>Version '.HESK_NEW_VERSION.' tables already exist!</b><br /><br />
        Your database seems to be compatible with HESK version '.HESK_NEW_VERSION.'<br /><br />
	    To install a new copy of HESK use the <a href="index.php">New install</a> option instead.');
    }
	elseif ($problem == 5)
	{
		hesk_show_error('MySQL version <b>'.REQUIRE_MYSQL_VERSION.'+</b> required, you are using: <b>' . MYSQL_VERSION . '</b><br /><br />
		You are using and old and insecure MySQL version with known bugs, security issues and outdated functionality.<br /><br />
		Ask your hosting company to update your MySQL version.');
	}
    elseif ($problem == 6)
    {
        hesk_show_notice('Please select your help desk timezone'); 
    }
    else
    {
    	hesk_show_notice('Contact your host for help with correct database settings', 'Tip');
    }
	?>

	<div align="center">
	<table border="0" width="750" cellspacing="1" cellpadding="5" class="white">
	<tr>
	<td>

	<form action="<?php echo INSTALL_PAGE; ?>" method="post">
	<table>
	<tr>
	<td width="200">Database Host:</td>
	<td><input type="text" name="host" value="<?php echo $hesk_settings['db_host']; ?>" size="40" autocomplete="off" /></td>
	</tr>
	<tr>
	<td width="200">Database Name:</td>
	<td><input type="text" name="name" value="<?php echo $hesk_settings['db_name']; ?>" size="40" autocomplete="off" /></td>
	</tr>
	<tr>
	<td width="200">Database User (login):</td>
	<td><input type="text" name="user" value="<?php echo str_replace('&', '&amp;', $hesk_settings['db_user']); ?>" size="40" autocomplete="off" /></td>
	</tr>
	<tr>
	<td width="200">User Password:</td>
	<td><input type="text" name="pass" value="<?php echo str_replace(array('&', '>', '<'), array('&amp;', '&gt;', '&lt;'), $hesk_settings['db_pass']); ?>" size="40" autocomplete="off" /></td>
	</tr>
	<?php
	if (INSTALL_PAGE == 'install.php')
	{
		?>
		<tr>
		<td width="200">Table prefix:</td>
		<td><input type="text" name="pfix" value="<?php echo $hesk_settings['db_pfix']; ?>" size="40" autocomplete="off" /></td>
		</tr>

		</table>

		<hr />

		<script language="javascript" type="text/javascript"><!--
		function hesk_randomPassword()                                                            
		{
			chars = '23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ!@#$%*()_+-={}[]:?,.';
			length = Math.floor(Math.random() * (5)) + 10;
			var result = '';
			for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
			return result;
		}
		//-->
		</script>

		<h3>HESK login details</h3>

        <p>Set the name, email, username and password of the main HESK Administrator.</p>

		<table>
        <tr>
        <td width="200">Admin name:</td>
        <td><input type="text" name="admin_name" value="<?php echo isset($_SESSION['admin_name']) ? stripslashes($_SESSION['admin_name']) : 'Your name'; ?>" size="40" autocomplete="off" /></td>
        </tr>
        <tr>
        <tr>
        <td width="200">Admin email:</td>
        <td><input type="text" name="admin_email" value="<?php echo isset($_SESSION['admin_email']) ? stripslashes($_SESSION['admin_email']) : 'you@example.com'; ?>" size="40" autocomplete="off" /></td>
        </tr>
        <tr>
		<tr>
		<td width="200">Choose a Username:</td>
		<td><input type="text" name="admin_user" value="<?php echo isset($_SESSION['admin_user']) ? stripslashes($_SESSION['admin_user']) : 'Administrator'; ?>" size="40" autocomplete="off" /></td>
		</tr>
		<tr>
		<td width="200">Choose a Password:</td>
		<td><input type="text" name="admin_pass" id="admin_pass" value="<?php echo isset($_SESSION['admin_pass']) ? stripslashes($_SESSION['admin_pass']) : ''; ?>" size="40" autocomplete="off" /></td>
		</tr>
		<tr>
		<td width="200">&nbsp;</td>
		<td style="text-align:right"><a href="javascript:void(0)" onclick="javascript:getElementById('admin_pass').value = hesk_randomPassword();">Generate a random password</a></td>
		</tr>
		</table>

		<?php
	}
	else
	{
		?>
		</table>
		<?php
	}
	?>

    <hr />

    <h3>Other info</h3>

    <table>
    <tr>
    <td width="200">Help desk timezone:</td>
    <td>
    <?php
    // Get list of supported timezones
    $timezone_list = hesk_generate_timezone_list();
    ?>
    <select name="timezone">
    <?php
    foreach ($timezone_list as $timezone => $description)
    {
        echo '<option value="' . $timezone . '"' . ($hesk_settings['timezone'] == $timezone ? ' selected="selected"' : '') . '>' . $description . '</option>';
    }
    ?>
    </select>
    </td>
    </tr>
    </table>

    <p>&nbsp;</p>

	<p align="center"><input type="hidden" name="dbtest" value="1" /><input type="submit" value="Continue to Step 4" class="orangebutton" onmouseover="hesk_btn(this,'orangebuttonover');" onmouseout="hesk_btn(this,'orangebutton');" /></p>
	</form>

	</td>
	</tr>
	</table>
	</div>

	<?php
    hesk_iFooter();
} // End hesk_iDatabase()


function hesk_iCheckSetup()
{
    global $hesk_settings;

    $correct_these = array();

    // 1. PHP 5+ required
    if ( function_exists('version_compare') && version_compare(PHP_VERSION,REQUIRE_PHP_VERSION,'<') )
    {
		$correct_these[] = '
		PHP version <b>'.REQUIRE_PHP_VERSION.'+</b> required, you are using: <b>' . PHP_VERSION . '</b><br /><br />
		You are using and old and insecure PHP version with known bugs, security issues and outdated functionality.<br /><br />
		Ask your hosting company to update your PHP version.
		';
    }

    // 2. json_encode / json_decode must be available
    if (!extension_loaded('json')) {
        $correct_these[] = 'The JSON PHP extension is required. Ask your hosting company about how to have the JSON extension enabled.';
    }

    // 3. File hesk_settings.inc.php must be writable
	if ( ! is__writable(HESK_PATH . 'hesk_settings.inc.php') )
	{
		// -> try to CHMOD it
		if ( function_exists('chmod') )
		{
			@chmod(HESK_PATH . 'hesk_settings.inc.php', 0666);
		}

		// -> test again
		if ( ! is__writable(HESK_PATH . 'hesk_settings.inc.php') )
		{
			$correct_these[] = '
			File <b>hesk_settings.inc.php</b> is not writable by PHP.<br /><br />
			Make sure PHP has permission to write to file <b>hesk_settings.inc.php</b><br /><br />
			&raquo; on <b>Linux</b> servers <a href="https://www.phpjunkyard.com/tutorials/ftp-chmod-tutorial.php">CHMOD</a> this file to 666 (rw-rw-rw-)<br />
	        &raquo; on <b>Windows</b> servers allow Internet Guest Account to modify the file<br />
	        &raquo; contact your hosting company for help with setting up file permissions.
			';
		}
	}

    // 4. Folder attachments must exist
    $hesk_settings['attach_dir_name'] = isset($hesk_settings['attach_dir']) ? $hesk_settings['attach_dir'] : 'attachments';
    $hesk_settings['attach_dir'] = HESK_PATH . $hesk_settings['attach_dir_name'];

	// -> Try to create it
	if ( ! file_exists($hesk_settings['attach_dir']) )
	{
	    @mkdir($hesk_settings['attach_dir'], 0755);
	}

    // -> Is the folder now there?
	if ( is_dir($hesk_settings['attach_dir']) )
    {

		// -> Is it writable?
	    if ( ! is__writable($hesk_settings['attach_dir']) )
	    {
			// -> try to CHMOD it
			@chmod($hesk_settings['attach_dir'], 0777);

			// -> test again
			if ( ! is__writable($hesk_settings['attach_dir']) )
			{
				$correct_these[] = '
				Folder <b>' . $hesk_settings['attach_dir_name'] . '</b> is not writable by PHP.<br /><br />
				Make sure PHP has permission to write to folder <b>' . $hesk_settings['attach_dir_name'] . '</b><br /><br />
				&raquo; on <b>Linux</b> servers <a href="https://www.phpjunkyard.com/tutorials/ftp-chmod-tutorial.php">CHMOD</a> this folder to 777 (rwxrwxrwx)<br />
		        &raquo; on <b>Windows</b> servers allow Internet Guest Account to modify the folder<br />
		        &raquo; contact your hosting company for help with setting up folder permissions.
				';
		   	}
	    }
	}
	else
	{
		$correct_these[] = '
		Folder <b>' . $hesk_settings['attach_dir_name'] . '</b> is missing.<br /><br />
		Create a folder called <b>' . $hesk_settings['attach_dir_name'] . '</b> inside your main HESK folder.<br /><br />
		';
	}

	// 4.2 Folder cache must exist
	$hesk_settings['cache_dir_name'] = isset($hesk_settings['cache_dir']) ? $hesk_settings['cache_dir'] : 'cache';
	$hesk_settings['cache_dir'] = HESK_PATH . $hesk_settings['cache_dir_name'];

	// -> Try to create it
	if ( ! file_exists($hesk_settings['cache_dir']) )
	{
	    @mkdir($hesk_settings['cache_dir'], 0755);
	}

	// -> Is the folder now there?
	if ( is_dir($hesk_settings['cache_dir']) )
	{

		// -> Is it writable?
		if ( ! is__writable($hesk_settings['cache_dir']) )
		{
			// -> try to CHMOD it
			@chmod($hesk_settings['cache_dir'], 0777);

			// -> test again
			if ( ! is__writable($hesk_settings['cache_dir']) )
			{
				$correct_these[] = '
				Folder <b>' . $hesk_settings['cache_dir_name'] . '</b> is not writable by PHP.<br /><br />
				Make sure PHP has permission to write to folder <b>' . $hesk_settings['cache_dir_name'] . '</b><br /><br />
				&raquo; on <b>Linux</b> servers <a href="https://www.phpjunkyard.com/tutorials/ftp-chmod-tutorial.php">CHMOD</a> this folder to 777 (rwxrwxrwx)<br />
				&raquo; on <b>Windows</b> servers allow Internet Guest Account to modify the folder<br />
				&raquo; contact your hosting company for help with setting up folder permissions.
				';
			}
		}
	}
	else
	{
		$correct_these[] = '
		Folder <b>' . $hesk_settings['cache_dir_name'] . '</b> is missing.<br /><br />
		Create a folder called <b>' . $hesk_settings['cache_dir_name'] . '</b> inside your main HESK folder.<br /><br />
		';
	}

    // 5. MySQL must be available
	if ( ! function_exists('mysql_connect') && ! function_exists('mysqli_connect') )
	{
		$correct_these[] = '
		MySQL is disabled.<br /><br />
		HESK requires MySQL to be installed and enabled.<br /><br />
        Ask your hosting company to enable MySQL for PHP.
		';
	}

    // 6. Can we use GD library?
	$GD_LIB = ( extension_loaded('gd') && function_exists('gd_info') ) ? true : false;

	// 7. Make sure old files are deleted
	$hesk_settings['admin_dir'] = isset($hesk_settings['admin_dir']) ? $hesk_settings['admin_dir'] : 'admin';
	$old_files = array(

	    // pre-0.93 *.inc files
	    'hesk_settings.inc','hesk.sql','inc/common.inc','inc/database.inc','inc/footer.inc','inc/header.inc',
	    'inc/print_tickets.inc','inc/show_admin_nav.inc','inc/show_search_form.inc','install.php','update.php',

		// pre-2.0 files
		'admin.php','admin_change_status.php','admin_main.php','admin_move_category','admin_reply_ticket.php',
	    'admin_settings.php','admin_settings_save.php','admin_ticket.php','archive.php',
	    'delete_tickets.php','find_tickets.php','manage_canned.php','manage_categories.php',
	    'manage_users.php','show_tickets.php',

		// pre-2.1 files
		'emails/','language/english.php',

	    // pre-2.3 files
	    'secimg.inc.php',

	    // pre-2.4 files
	    'hesk_style_v23.css','TreeMenu.js',

        // malicious files that were found on some websites illegally redistributing HESK
        'inc/tiny_mce/utils/r00t10.php', 'language/en/help_files/r00t10.php',

        // pre-2.5 files
        'hesk_style_v24.css', 'hesk_javascript_v24.js',

        // pre-2.6 files
        'hesk_style_v25.css', 'hesk_javascript_v25.js',

		// pre-2.7 files,
		$hesk_settings['admin_dir'].'/options.php',

        // pre-3.0 files,
        $hesk_settings['admin_dir'].'/admin_settings.php',
        'img/add_article.png',
        'img/add_category.png',
        'img/anonymize.png',
        'img/article_text.png',
        'img/autoassign_off.png',
        'img/autoassign_on.png',
        'img/ban.png',
        'img/banned.png',
        'img/blank.gif',
        'img/bluebtn.png',
        'img/clip.png',
        'img/code.png',
        'img/code_off.png',
        'img/delete.png',
        'img/delete_off.png',
        'img/delete_ticket.png',
        'img/edit.png',
        'img/email.png',
        'img/error.png',
        'img/existingticket.png',
        'img/export.png',
        'img/flag_critical.png',
        'img/flag_high.png',
        'img/flag_low.png',
        'img/flag_low2.png',
        'img/flag_medium.png',
        'img/folder-expanded.gif',
        'img/folder.gif',
        'img/greenbtn.jpg',
        'img/greenbtnover.gif',
        'img/header.png',
        'img/headerbgsm.jpg',
        'img/headerleftsm.jpg',
        'img/headerrightsm.jpg',
        'img/header_bottom.png',
        'img/header_bottom_left.png',
        'img/header_bottom_right.png',
        'img/header_left.png',
        'img/header_right.png',
        'img/header_top.png',
        'img/header_up_left.png',
        'img/header_up_right.png',
        'img/ico-search.png',
        'img/ico_canned.gif',
        'img/ico_categories.gif',
        'img/ico_home.gif',
        'img/ico_kb.gif',
        'img/ico_logout.gif',
        'img/ico_mail.gif',
        'img/ico_profile.gif',
        'img/ico_reports.gif',
        'img/ico_settings.gif',
        'img/ico_tools.png',
        'img/ico_users.gif',
        'img/import_kb.png',
        'img/import_kb1.png',
        'img/inbox.png',
        'img/info.png',
        'img/link.png',
        'img/lock.png',
        'img/login.png',
        'img/mail.png',
        'img/manage.png',
        'img/menu.png',
        'img/move_down.png',
        'img/move_down1.png',
        'img/move_down2.png',
        'img/move_down3.png',
        'img/move_down4.png',
        'img/move_down5.png',
        'img/move_down6.png',
        'img/move_down7.png',
        'img/move_down8.png',
        'img/move_down9.png',
        'img/move_up.png',
        'img/move_up1.png',
        'img/move_up2.png',
        'img/move_up3.png',
        'img/move_up4.png',
        'img/move_up5.png',
        'img/move_up6.png',
        'img/move_up7.png',
        'img/move_up8.png',
        'img/move_up9.png',
        'img/newticket.png',
        'img/new_mail.png',
        'img/notice.png',
        'img/online_off.png',
        'img/online_on.png',
        'img/orangebtn.jpg',
        'img/orangebtnover.gif',
        'img/orangebtnsec.jpg',
        'img/outbox.png',
        'img/print.png',
        'img/private.png',
        'img/public.png',
        'img/refresh.png',
        'img/reload.png',
        'img/roundcornersb.jpg',
        'img/roundcornerslb.jpg',
        'img/roundcornerslm.jpg',
        'img/roundcornerslt.jpg',
        'img/roundcornersrb.jpg',
        'img/roundcornersrm.jpg',
        'img/roundcornersrt.jpg',
        'img/roundcornerst.jpg',
        'img/sort_priority_asc.png',
        'img/sort_priority_desc.png',
        'img/star_0.png',
        'img/star_10.png',
        'img/star_15.png',
        'img/star_20.png',
        'img/star_25.png',
        'img/star_30.png',
        'img/star_35.png',
        'img/star_40.png',
        'img/star_45.png',
        'img/star_50.png',
        'img/sticky.png',
        'img/sticky_off.png',
        'img/success.png',
        'img/tableheader.jpg',
        'img/tag.png',
        'img/tag_off.png',
        'img/unlock.png',
        'img/vertical.jpg',
        'img/view.png',
	    );

	sort($old_files);

	$still_exist = array();

	foreach ($old_files as $f)
	{
		if (file_exists(HESK_PATH . $f))
	    {
            //Try to remove the file
            @unlink(HESK_PATH . $f);

            // If not successful, ask the user to delete those files
            if (file_exists(HESK_PATH . $f))
            {
	    	    $still_exist[] = $f;
            }
	    }
	}

    $old_folders = array(
        // pre-2.4 folders
        'help_files/',

        // pre-3.0 folders
        'inc/calendar/',
    );

	foreach ($old_folders as $f)
	{
		if (is_dir(HESK_PATH . $f))
	    {
            //Try to remove the folder
            hesk_rrmdir(HESK_PATH . $f);

            // If not successful, ask the user to delete those folders
            if (is_dir(HESK_PATH . $f))
            {
	    	    $still_exist[] = $f;
            }
	    }
	}

	if ( count($still_exist) )
	{
        sort(array_unique($still_exist));

		$correct_these[] = '
		Outdated files and folders<br /><br />
		For security reasons please delete these legacy files and folders:<br />
        <ul><li><b>'.implode('</b></li><li><b>',$still_exist).'</b></li></ul>
		';
	}

    // Do we have any errors?
    if ( count($correct_these) )
    {
		hesk_iHeader();
        ?>

        &nbsp;

		<?php
        foreach ($correct_these as $correct_this)
        {
        	hesk_show_error($correct_this);
            echo "&nbsp;";
        }
        ?>

		<form method="post" action="<?php echo INSTALL_PAGE; ?>">
		<p align="center"><input type="submit" onclick="javascript:this.value='Working, please wait...'" value="Click here to TEST AGAIN" class="orangebutton" onmouseover="hesk_btn(this,'orangebuttonover');" onmouseout="hesk_btn(this,'orangebutton');" /></p>
		</form>
        <p>&nbsp;</p>
        <?php
        hesk_iFooter();
    }

    // If all tests were successful, we can continue to the next step
    $_SESSION['set_attachments'] = 1;
	$_SESSION['set_captcha'] = $GD_LIB ? 1 : 0;
	$_SESSION['use_spamq'] = $GD_LIB ? 0 : 1;
	$_SESSION['step'] = 3;

	// When updating, first try saved MySQL info
	if (INSTALL_PAGE == 'update.php')
	{
		header('Location: ' . INSTALL_PAGE);
	}
	else
	{
		hesk_iDatabase();
	}
	exit();
}


function hesk_iStart()
{
	global $hesk_settings;

	// Set this session variable to check later if sessions are working
	$_SESSION['works'] = true;

	hesk_iHeader();

    $eula_alt =  '
        <div style="text-align:justify; width:100%">
        <p><b>HESK Software End User License Agreement</b></p>
        <p><a href="https://www.hesk.com/eula.php" target="_blank">Read the HESK End-User License Agreement here</a></p>
        </div>
        <p>&nbsp;</p>
        <p>&nbsp;</p>
    ';

    echo file_exists('../docs/license.html') ? '<iframe src="../docs/license.html" style="border:1px solid #ccc; height:250px; width:100%">'.$eula_alt.'</iframe>' : $eula_alt;
	?>

<p>&nbsp;</p>

<form method="post" action="<?php echo INSTALL_PAGE; ?>" name="license">
<div align="center">
<table border="0">
<tr>
<td>

    <div id="update">
	<p><b>Do you accept the HESK Software End-User License Agreement?</b><br />&nbsp;</p>

	<p align="center">
	<input type="hidden" name="agree" value="YES" />
	<input type="button" onclick="javascript:parent.location='index.php'" value="I DO NOT ACCEPT (Cancel setup)" class="orangebuttonsec" onmouseover="hesk_btn(this,'orangebuttonsecover');" onmouseout="hesk_btn(this,'orangebuttonsec');" />
	&nbsp;
	<input type="submit" onclick="javascript:this.value='Working, please wait...';document.getElementById('update').style.display='none';document.getElementById('working').style.display='block';" value="I ACCEPT (Click to continue) &raquo;" class="orangebutton" onmouseover="hesk_btn(this,'orangebuttonover');" onmouseout="hesk_btn(this,'orangebutton');" />
	</p>

    </div>

    <div id="working" style="texyt-align:center; display:none">
    <b>Updating, please wait...</b>
    </div>

    <p><img src="https://www.hesk.com/images/space.gif" width="10" height="10" alt="" border="0" />&nbsp;</p>

</td>
</tr>
</table>
</div>
</form>

	<?php
    hesk_iFooter();
} // End hesk_iStart()


function hesk_iHeader()
{
    global $hesk_settings;

	$steps = array(
    	1 => '1. License agreement',
        2 => '2. Check setup',
        3 => '3. Setup Database',
        4 => '4. Customer Migration',
        5 => '5. Finishing touches'
        );

	?>
	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
	<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
	<title>HESK setup script: <?php echo HESK_NEW_VERSION; ?></title>
	<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
	<link href="hesk_style.css?<?php echo HESK_NEW_VERSION; ?>" type="text/css" rel="stylesheet" />
    <script type="text/javascript" src="jquery-3.5.1.min.js?<?php echo HESK_NEW_VERSION; ?>"></script>
	<script type="text/javascript" src="hesk_javascript.js?<?php echo HESK_NEW_VERSION; ?>"></script>
	</head>
	<body>

	<div align="center">
	<table border="0" cellspacing="0" cellpadding="5" class="enclosing">
	<tr>
	<td>
		<table width="100%" border="0" cellspacing="0" cellpadding="0">
		<tr>
		<td width="3"><img src="img/headerleftsm.jpg" width="3" height="25" alt="" /></td>
		<td class="headersm">HESK setup script: <?php echo HESK_NEW_VERSION; ?></td>
		<td width="3"><img src="img/headerrightsm.jpg" width="3" height="25" alt="" /></td>
		</tr>
		</table>
	</td>
	</tr>
	<tr>
	<td>

    <?php
    if ( isset($_SESSION['step']) )
    {
    	$_SESSION['step'] = intval($_SESSION['step']);
    	?>

		<table border="0" width="100%">
		<tr>
		<td>
        <?php
        foreach ($steps as $number => $description)
        {
        	if ($number == $_SESSION['step'])
            {
            	$steps[$number] = '<b>' . $steps[$number] . '</b>';
            }
            elseif ($number < $_SESSION['step'])
            {
            	$steps[$number] = '<span style="color:#008000">' . $steps[$number] . '</span>';
            }
        }

        echo implode(' &raquo; ', $steps);
        ?>
        </td>
		</tr>
		</table>

		<br />
	<?php
    }
    else
    {
		hesk_show_notice('<a href="../docs/index.html">Read installation guide</a> before using this setup script!', 'Important');
    }
    ?>

    <div align="center">
	<table border="0" cellspacing="0" cellpadding="0" width="100%">
	<tr>
		<td width="7" height="7"><img src="img/roundcornerslt.jpg" width="7" height="7" alt="" /></td>
		<td class="roundcornerstop"></td>
		<td><img src="img/roundcornersrt.jpg" width="7" height="7" alt="" /></td>
	</tr>
	<tr>
		<td class="roundcornersleft">&nbsp;</td>
		<td>
	<?php
} // End hesk_iHeader()


function hesk_iFooter()
{
	global $hesk_settings;
	?>
		</td>
		<td class="roundcornersright">&nbsp;</td>
	</tr>
	<tr>
		<td><img src="img/roundcornerslb.jpg" width="7" height="7" alt="" /></td>
		<td class="roundcornersbottom"></td>
		<td width="7" height="7"><img src="img/roundcornersrb.jpg" width="7" height="7" alt="" /></td>
	</tr>
	</table>
    </div>

	<p style="text-align:center"><span class="smaller">&nbsp;<br />Powered by <a href="https://www.hesk.com" class="smaller" title="Free PHP Help Desk Software">Help Desk Software</a> <b>HESK</b></span></p>
	</td>
	</tr>
	</table>
	</div>
	</body>
	</html>
	<?php
    exit();
} // End hesk_iFooter()


function hesk_iSessionError()
{
	hesk_session_stop();
	hesk_iHeader();
	?>

	<br />
	<div class="error">
		<img src="<?php echo HESK_PATH; ?>install/img/error.png" width="16" height="16" border="0" alt="" style="vertical-align:text-bottom" />
		<b>Error:</b> PHP sessions not working!<br /><br />Note that this is a server configuration issue, not a HESK issue.<br /><br />Please contact your hosting company and ask them to verify why PHP sessions aren't working on your server!
	</div>
	<br />

	<form method="get" action="<?php echo INSTALL_PAGE; ?>">
	<p align="center"><input type="submit" value="&laquo; Start over" class="orangebutton" onmouseover="hesk_btn(this,'orangebuttonover');" onmouseout="hesk_btn(this,'orangebutton');" /></p>
	</form>

	<?php
	hesk_iFooter();
} // END hesk_iSessionError()


function hesk_compareVariable($k,$v)
{
	global $hesk_settings;

    if (is_array($v))
    {
    	foreach ($v as $sub_k => $sub_v)
        {
			$v[$k] = hesk_compareVariable($sub_k,$sub_v);
        }
    }

	if (isset($hesk_settings[$k]))
    {
    	return $hesk_settings[$k];
    }
    else
    {
    	return $v;
    }
} // END hesk_compareVariable()

function hesk_iCustomerMigrationIntro() {
    global $hesk_settings;

    hesk_dbConnect();

    hesk_iHeader();
    ?>

<h3>Customer Migration</h3>

<br />

    <div align="center">
    <table border="0" width="750" cellspacing="1" cellpadding="5" class="white">
    <tr>
    <td>
        <div id="intro">
            <p>As of HESK 3.5.0, customer information is stored differently and requires a migration process.</p>
            <p>Number of customers to migrate: <span id="customer-intro-count">...</span></p>
            <noscript>
                In order to continue, you must have Javascript enabled.
            </noscript>
            <div class="lds-dual-ring" id="intro-loader"></div>
            <div id="continue-block" style="display: none">
                <p>Click the "Migrate" button below to start the migration process.  This page will automatically update during this process.</p>
                <button type="button"
                        class="orangebutton"
                        onmouseover="hesk_btn(this,'orangebuttonover');"
                        onmouseout="hesk_btn(this,'orangebutton');"
                        onclick="beginCustomerMigration()">Migrate</button>
            </div>
        </div>
        <div id="migrating" style="display: none">
            <?php hesk_show_notice('The migration process has begun.  Please do <b>not</b> close this window or refresh the page.'); ?>
            <p>Migrating...</p>
            <div style="border: 1px solid #d4d6e3; width: 100%; height: 14px">
                <?php // Update width as progress is made ?>
                <div class="progress-bar" style="font-size: 1px; height: 14px; width: 0; background-color: #008000; border: none;">
                </div>
            </div>
            <div class="progress-values">
                <span id="count-completed">0</span> / <span id="count-total">0</span>
            </div>
        </div>
        <div id="complete" style="display: none">
            <p>The customer migration process is complete!</p>
            <form method="get" action="<?php echo INSTALL_PAGE; ?>">
                <input type="hidden" name="migration-complete" value="1">
                <button type="submit"
                        class="orangebutton"
                        onmouseover="hesk_btn(this,'orangebuttonover');"
                        onmouseout="hesk_btn(this,'orangebutton');">Continue</button>
            </form>
        </div>
    </td>
    </tr>
    </table>
    </div>
    <script>
        getCustomerCount();
    </script>
<?php
    hesk_iFooter();
}

function hesk_iDropOldPreCustomerColumns() {
    global $hesk_settings;

    $column_exists = hesk_dbQuery("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS 
         WHERE TABLE_SCHEMA = '".hesk_dbEscape($hesk_settings['db_name'])."' 
           AND TABLE_NAME = '".hesk_dbEscape($hesk_settings['db_pfix'])."replies' 
           AND COLUMN_NAME = 'name'");
    if (hesk_dbNumRows($column_exists) > 0) {
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` DROP COLUMN `name`");
    }
}


function is__writable($path)
{
//will work in despite of Windows ACLs bug
//NOTE: use a trailing slash for folders!!!
//see http://bugs.php.net/bug.php?id=27609
//see http://bugs.php.net/bug.php?id=30931

    if ($path[strlen($path)-1]=='/') // recursively return a temporary file path
        return is__writable($path.uniqid(mt_rand()).'.tmp');
    else if (is_dir($path))
        return is__writable($path.'/'.uniqid(mt_rand()).'.tmp');
    // check tmp file for read/write capabilities
    $rm = file_exists($path);
    $f = @fopen($path, 'a');
    if ($f===false)
        return false;
    fclose($f);
    if (!$rm)
        unlink($path);
    return true;
} // END is__writable()


function hesk_getLanguageForFile($set) {
    global $hesk_settings;

    $languages = '';
    foreach ($hesk_settings['languages'] as $name => $info) {
        $languages .= "'".addslashes($name)."' => array('folder'=>'".$info['folder']."','hr'=>'".addslashes($info['hr'])."'),\n";
    }

    return $languages;
} // END hesk_getLanguageForFile()
jquery-3.5.1.min.js
wget 'https://lists2.roe3.org/hesk/_install/jquery-3.5.1.min.js'
View Content
/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
update.php
wget 'https://lists2.roe3.org/hesk/_install/update.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','../');

define('INSTALL_PAGE', 'update.php');
require(HESK_PATH . 'install/install_functions.inc.php');

// Convert old database settings
if (isset($hesk_settings['database_user']))
{
	$hesk_settings['db_user'] = $hesk_settings['database_user'];
	$hesk_settings['db_name'] = $hesk_settings['database_name'];
	$hesk_settings['db_pass'] = $hesk_settings['database_pass'];
	$hesk_settings['db_host'] = $hesk_settings['database_host'];
}

// Set the table prefix to default for versions older than 2.0
if ( ! isset($hesk_settings['db_pfix']))
{
	$hesk_settings['db_pfix'] = 'hesk_';
}

// If no step is defined, start with step 1
if ( ! isset($_SESSION['step']) )
{
    $_SESSION['step']=1;
}
// Check if the license has been agreed to and verify sessions are working
elseif ($_SESSION['step']==1)
{
    $agree = hesk_POST('agree', '');
    if ($agree == 'YES')
    {
		// Are sessions working?
		if ( empty($_SESSION['works']) )
        {
        	hesk_iSessionError();
        }

		// All OK, continue
        $_SESSION['license_agree']=1;
        $_SESSION['step']=2;
    }
    else
    {
        $_SESSION['step']=1;
    }
}

// Test database connection?
if ($_SESSION['step'] == 3)
{
    // Test MySQL connection
    if (isset($_POST['dbtest']))
    {
        // Get timezone info
        $hesk_settings['timezone'] = hesk_input( hesk_POST('timezone') );
        if ( ! in_array($hesk_settings['timezone'], timezone_identifiers_list()) )
        {
            $hesk_settings['timezone'] = 'UTC';
        }

        // Test MySQL connection using provided data
        $hesk_db_link = hesk_iTestDatabaseConnection();
    }
    else
    {
       // Force selecting a timezone for versions prior to 2.8.0
       if (version_compare(HESK_NEW_VERSION,'2.8.0','<='))
       {
           hesk_iDatabase(6);
       }

       // Test MySQL connection using saved data
       $hesk_db_link = hesk_iTestDatabaseConnection(true);
    }

	// Detect which version we are updating from
	$hesk_settings['update_from'] = hesk_iDetectVersion();

	// Is the installed version current?
	if ($hesk_settings['update_from'] == HESK_NEW_VERSION)
    {
		hesk_iDatabase(4);
    }

    // Do we need to update languages?
    if ($hesk_settings['language_backup'] != 'English' || count($hesk_settings['languages_backup']) > 1) {
        $hesk_settings['language'] = $hesk_settings['language_backup'];
        $hesk_settings['languages'] = $hesk_settings['languages_backup'];

        ob_start();
        $_SESSION['language_updated'] = hesk_iUpdateLangauges();
        ob_end_clean();

        if ( ! $_SESSION['language_updated']) {
            $hesk_settings['language'] = 'English';
            $hesk_settings['languages'] = array('English' => array('folder'=>'en','hr'=>'------ Reply above this line ------'));
       }
    }

	// All ok, let's save settings
	hesk_iSaveSettings();

	// Now update HESK database tables
	hesk_iUpdateTables();

    // Do we need to migrate customers?
    $ticket_count = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` LIMIT 1");
    $any_tickets = hesk_dbNumRows($ticket_count) > 0;

    if ($any_tickets) {
        $_SESSION['step']=4;
    } else {
        hesk_iDropOldPreCustomerColumns();
        $_SESSION['step']=5;
    }
}

if ($_SESSION['step'] == 4) {
    // Have customer accounts been migrated?
    if (hesk_REQUEST('migration-complete')) {
        $_SESSION['step'] = 5;
    } else {
        $reply_name_column_gone = hesk_dbQuery("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS 
         WHERE TABLE_SCHEMA = '" . hesk_dbEscape($hesk_settings['db_name']) . "' 
           AND TABLE_NAME = '" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies' 
           AND COLUMN_NAME = 'name'");

        if (hesk_dbNumRows($reply_name_column_gone) === 0) {
            $_SESSION['step'] = 5;
        }
    }
}

// Which step are we at?
switch ($_SESSION['step'])
{
	case 2:
	   hesk_iCheckSetup();
	   break;
	case 3:
	   hesk_iDatabase();
	   break;
    case 4:
       hesk_iCustomerMigrationIntro();
       break;
	case 5:
	   hesk_iFinish();
	   break;
	default:
	   hesk_iStart();
}


// ******* FUNCTIONS ******* //


function hesk_iFinish()
{
    global $hesk_settings;
    hesk_purge_cache();
    hesk_iHeader();
	?>

	<h3>Update database tables</h3>

	<br />
	<?php hesk_show_success('Congratulations, your HESK has been updated to ' . HESK_NEW_VERSION); ?>

    <?php
    if (defined('RECAPTCHA_V1'))
    {
        hesk_show_notice('reCaptcha V1 is no longer supported and has been disabled.<br /><br />Please enable a different anti-SPAM measure in HESK settings.');
    }

    if (isset($_SESSION['language_updated'])) {
        if ($_SESSION['language_updated']) {
            hesk_show_success('Language files updated');
        } else {
            hesk_show_notice('Could not automatically update language files.<br /><br />Your language has been reset to English.<br /><br />Login to your Hesk admin panel &gt; Settings &gt; General and update your language installations there.');
        }
        unset($_SESSION['language_updated']);
    }
    ?>

    <h3>Things to do next:</h3>

    <ol>

    <li><span style="color:#ff0000">Delete the <b>/install</b> folder from your server!</span><br />&nbsp;</li>

    <li>Login to HESK administration panel and make sure everything works fine.<br /><br />

	<form action="<?php echo HESK_PATH . $hesk_settings['admin_dir']; ?>/admin_main.php" method="get">
	<input type="submit" value="Continue to admin panel &raquo;" class="orangebutton" onmouseover="hesk_btn(this,'orangebuttonover');" onmouseout="hesk_btn(this,'orangebutton');" /></p>
	</form>

    </li>

    </ol>

    <p>&nbsp;</p>

	<?php
    hesk_iFooter();
} // End hesk_iFinish()


function hesk_iUpdateTables()
{
	global $hesk_db_link, $hesk_settings, $hesklang;

    $update_all_next = 0;

    // Force update possible MySQL strict issues before attempting anything else
    if (in_array($hesk_settings['update_from'], array('2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '0.94.1', '0.94', '0.91-0.93.1', '0.90')))
    {
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `dt` `dt` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'");
    }

	// Updating version 0.90 to 0.91
	if ($hesk_settings['update_from'] == '0.90')
	{
		hesk_dbQuery("ALTER TABLE `hesk_users` ADD `notify` CHAR( 1 ) DEFAULT '1' NOT NULL");
        $update_all_next = 1;
	} // END version 0.90 to 0.91


	// Updating versions 0.91 through 0.93.1 to 0.94
	if ($update_all_next || $hesk_settings['update_from'] == '0.91-0.93.1')
	{
		hesk_dbQuery("CREATE TABLE `hesk_attachments` (
		  `att_id` mediumint(8) unsigned NOT NULL auto_increment,
		  `ticket_id` varchar(10) NOT NULL default '',
		  `saved_name` varchar(255) NOT NULL default '',
		  `real_name` varchar(255) NOT NULL default '',
		  `size` int(10) unsigned NOT NULL default '0',
		  PRIMARY KEY  (`att_id`),
		  KEY `ticket_id` (`ticket_id`)
		) ENGINE=MyISAM");

		hesk_dbQuery("CREATE TABLE `hesk_std_replies` (
		`id` smallint(5) unsigned NOT NULL auto_increment,
		`title` varchar(70) NOT NULL default '',
		`message` text NOT NULL,
		`reply_order` smallint(5) unsigned NOT NULL default '0',
		PRIMARY KEY  (`id`)
		) ENGINE=MyISAM");

		hesk_dbQuery("ALTER TABLE `hesk_categories`
		CHANGE `name` `name` varchar(60) NOT NULL default '',
		ADD `cat_order` smallint(5) unsigned NOT NULL default '0'");

		hesk_dbQuery("ALTER TABLE `hesk_replies`
		CHANGE `name` `name` varchar(50) NOT NULL default '',
		ADD `attachments` TEXT");

		hesk_dbQuery("ALTER TABLE `hesk_tickets`
		CHANGE `name` `name` varchar(50) NOT NULL default '',
		CHANGE `category` `category` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '1',
		CHANGE `priority` `priority` enum('1','2','3') NOT NULL default '3',
		CHANGE `subject` `subject` varchar(70) NOT NULL default '',
		ADD `lastchange` datetime NOT NULL AFTER `dt`,
		CHANGE `status` `status` enum('0','1','2','3') default '1',
		ADD `lastreplier` enum('0','1') NOT NULL default '0',
		ADD `archive` enum('0','1') NOT NULL default '0',
		ADD `attachments` text,
		ADD `custom1` VARCHAR( 255 ) NOT NULL default '',
		ADD `custom2` VARCHAR( 255 ) NOT NULL default '',
		ADD `custom3` VARCHAR( 255 ) NOT NULL default '',
		ADD `custom4` VARCHAR( 255 ) NOT NULL default '',
		ADD `custom5` VARCHAR( 255 ) NOT NULL default '',
		ADD INDEX `archive` ( `archive` )");

		// Change status of closed tickets to the new "Resolved" status
		hesk_dbQuery("UPDATE `hesk_tickets` SET `status`='3' WHERE `status`='0'");

		// Populate lastchange
		hesk_dbQuery("UPDATE `hesk_tickets` SET `lastchange`=`dt`");

		// Update categories with order values
		$res = hesk_dbQuery("SELECT `id` FROM `hesk_categories`");
		$i = 10;
		while ($mycat=hesk_dbFetchAssoc($res))
		{
			hesk_dbQuery("UPDATE `hesk_categories` SET `cat_order`=$i WHERE `id`=" . intval($mycat['id']));
			$i += 10;
		}

        $update_all_next = 1;
	} // END versions 0.91 through 0.93.1 to 0.94


    // Updating version 0.94 to 0.94.1
    if ($hesk_settings['update_from'] == '0.94')
    {
		hesk_dbQuery("CREATE TABLE `hesk_attachments` (
		  `att_id` mediumint(8) unsigned NOT NULL auto_increment,
		  `ticket_id` varchar(10) NOT NULL default '',
		  `saved_name` varchar(255) NOT NULL default '',
		  `real_name` varchar(255) NOT NULL default '',
		  `size` int(10) unsigned NOT NULL default '0',
		  PRIMARY KEY  (`att_id`),
		  KEY `ticket_id` (`ticket_id`)
		) ENGINE=MyISAM");

		if ($hesk_settings['attachments']['use'])
		{
			/* Update attachments for tickets */
			$res = hesk_dbQuery("SELECT * FROM `hesk_tickets` WHERE `attachments` != '' ");
			while ($ticket = hesk_dbFetchAssoc($res))
			{
				$att=explode('#####',substr($ticket['attachments'], 0, -5));
				$myattachments = '';
				foreach ($att as $myatt)
				{
					$name = substr(strstr($myatt, $ticket['trackid']),16);
					$saved_name = strstr($myatt, $ticket['trackid']);
					$size = filesize($hesk_settings['server_path'].'/attachments/'.$saved_name);

					hesk_dbQuery("INSERT INTO `hesk_attachments` (`ticket_id`,`saved_name`,`real_name`,`size`) VALUES ('".hesk_dbEscape($ticket['trackid'])."', '".hesk_dbEscape($saved_name)."', '".hesk_dbEscape($name)."', '".intval($size)."')");
					$myattachments .= hesk_dbInsertID() . '#' . $name .',';
				}

				hesk_dbQuery("UPDATE `hesk_tickets` SET `attachments` = '".hesk_dbEscape($myattachments)."' WHERE `id` = ".intval($ticket['id']));
			}

			// Update attachments for replies
			$res = hesk_dbQuery("SELECT * FROM `hesk_replies` WHERE `attachments` != '' ");
			while ($ticket = hesk_dbFetchAssoc($res))
			{
				$res2 = hesk_dbQuery("SELECT `trackid` FROM `hesk_tickets` WHERE `id` = '".intval($ticket['replyto'])."' LIMIT 1");
				$trackingID = hesk_dbResult($res2,0,0);

				$att=explode('#####',substr($ticket['attachments'], 0, -5));
				$myattachments = '';
				foreach ($att as $myatt)
                {
					$name = substr(strstr($myatt, $trackingID),16);
					$saved_name = strstr($myatt, $trackingID);
					$size = filesize($hesk_settings['server_path'].'/attachments/'.$saved_name);

					hesk_dbQuery("INSERT INTO `hesk_attachments` (`ticket_id`,`saved_name`,`real_name`,`size`) VALUES ('".hesk_dbEscape($trackingID)."', '".hesk_dbEscape($saved_name)."', '".hesk_dbEscape($name)."', '".intval($size)."')");
					$myattachments .= hesk_dbInsertID() . '#' . $name .',';
				}

				hesk_dbQuery("UPDATE `hesk_replies` SET `attachments` = '".hesk_dbEscape($myattachments)."' WHERE `id` = ".intval($ticket['id']));
			}
		}  // END if attachments use

        $update_all_next = 1;
    } // END version 0.94 to 0.94.1


	// Updating version 0.94.1 to 2.0
	if ($update_all_next || $hesk_settings['update_from'] == '0.94.1')
	{
		hesk_dbQuery("CREATE TABLE `hesk_kb_articles` (
		  `id` smallint(5) unsigned NOT NULL auto_increment,
		  `catid` smallint(5) unsigned NOT NULL default '0',
		  `dt` timestamp NOT NULL default CURRENT_TIMESTAMP,
		  `author` smallint(5) unsigned NOT NULL default '0',
		  `subject` varchar(255) NOT NULL default '',
		  `content` text NOT NULL,
		  `rating` float NOT NULL default '0',
		  `votes` mediumint(8) unsigned NOT NULL default '0',
		  `views` mediumint(8) unsigned NOT NULL default '0',
		  `type` enum('0','1','2') NOT NULL default '0',
		  `html` enum('0','1') NOT NULL default '0',
		  `art_order` smallint(5) unsigned NOT NULL default '0',
		  `history` text NOT NULL,
		  PRIMARY KEY  (`id`),
		  KEY `catid` (`catid`),
		  KEY `type` (`type`),
		  FULLTEXT KEY `subject` (`subject`,`content`)
		) ENGINE=MyISAM");

		hesk_dbQuery("CREATE TABLE `hesk_kb_categories` (
		  `id` smallint(5) unsigned NOT NULL auto_increment,
		  `name` varchar(255) NOT NULL default '',
		  `parent` smallint(5) unsigned NOT NULL default '0',
		  `articles` smallint(5) unsigned NOT NULL default '0',
		  `cat_order` smallint(5) unsigned NOT NULL default '0',
		  `type` enum('0','1') NOT NULL default '0',
		  PRIMARY KEY  (`id`),
		  KEY `type` (`type`)
		) ENGINE=MyISAM");

		hesk_dbQuery("INSERT INTO `hesk_kb_categories` VALUES (1, 'Knowledgebase', 0, 0, 10, '0')");

		hesk_dbQuery("CREATE TABLE `hesk_notes` (
		  `id` mediumint(8) unsigned NOT NULL auto_increment,
		  `ticket` mediumint(8) unsigned NOT NULL default '0',
		  `who` smallint(5) unsigned NOT NULL default '0',
		  `dt` datetime NOT NULL,
		  `message` text NOT NULL,
		  PRIMARY KEY  (`id`),
		  KEY `ticketid` (`ticket`)
		) ENGINE=MyISAM");

	    $sql = array();
		$sql[] = "ALTER TABLE `hesk_replies` ADD `staffid` SMALLINT UNSIGNED NOT NULL DEFAULT '0'";
		$sql[] = "ALTER TABLE `hesk_replies` ADD `rating` ENUM( '1', '5' ) default NULL";

		$sql[] = "ALTER TABLE `hesk_tickets` ADD INDEX `categories` ( `category` )";
		$sql[] = "ALTER TABLE `hesk_tickets` ADD INDEX `statuses` ( `status` ) ";
		$sql[] = "ALTER TABLE `hesk_tickets` CHANGE `custom1` `custom1` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` CHANGE `custom2` `custom2` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` CHANGE `custom3` `custom3` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` CHANGE `custom4` `custom4` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` CHANGE `custom5` `custom5` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` ADD `custom6` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` ADD `custom7` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` ADD `custom8` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` ADD `custom9` text NOT NULL";
		$sql[] = "ALTER TABLE `hesk_tickets` ADD `custom10` text NOT NULL";

		$sql[] = "ALTER TABLE `hesk_users` CHANGE `pass` `pass` CHAR( 40 ) NOT NULL";
		$sql[] = "ALTER TABLE `hesk_users` CHANGE `isadmin` `isadmin` ENUM( '0', '1' ) NOT NULL DEFAULT '0'";
		$sql[] = "ALTER TABLE `hesk_users` CHANGE `notify` `notify` ENUM( '0', '1' ) NOT NULL DEFAULT '1'";
		$sql[] = "ALTER TABLE `hesk_users` ADD `heskprivileges` VARCHAR( 255 ) NOT NULL";
		$sql[] = "ALTER TABLE `hesk_users` ADD `ratingneg` mediumint(8) unsigned NOT NULL default '0'";
		$sql[] = "ALTER TABLE `hesk_users` ADD `ratingpos` mediumint(8) unsigned NOT NULL default '0'";
		$sql[] = "ALTER TABLE `hesk_users` ADD `rating` float NOT NULL default '0'";
		$sql[] = "ALTER TABLE `hesk_users` ADD `replies` mediumint(8) unsigned NOT NULL default '0'";

		$sql[] = "ALTER TABLE `hesk_std_replies` CHANGE `title` `title` VARCHAR( 100 ) NOT NULL";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

	    // Update passwords to the new type and hesk privileges for non-admins */
		$res = hesk_dbQuery('SELECT `id`,`pass`,`isadmin` FROM `hesk_users` ORDER BY `id` ASC');

	    $sql = array();
		while ($row=hesk_dbFetchAssoc($res))
		{
			$new_pass = hesk_password_hash($row['pass']);
	        $s = "UPDATE `hesk_users` SET `pass`='".hesk_dbEscape($new_pass)."' ";
	        if ($row['isadmin'] == 0)
	        {
	        	$s .= ", `heskprivileges`='can_view_tickets,can_reply_tickets,can_change_cat,' ";
	        }
	        $s.= "WHERE `id`=".intval($row['id']);
	        $sql[] = $s;
		}

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

        $update_all_next = 1;
    } // END version 0.94.1 to 2.0


	// Updating version 2.0 to 2.1
	if ($update_all_next || $hesk_settings['update_from'] == '2.0')
	{
		hesk_dbQuery("CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_attachments` (
		  `att_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
		  `saved_name` varchar(255) NOT NULL DEFAULT '',
		  `real_name` varchar(255) NOT NULL DEFAULT '',
		  `size` int(10) unsigned NOT NULL DEFAULT '0',
		  PRIMARY KEY (`att_id`)
		) ENGINE=MyISAM");

		$sql = array();
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` ADD `attachments` TEXT NOT NULL";

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom11` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom12` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom13` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom14` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom15` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom16` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom17` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom18` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom19` text NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `custom20` text NOT NULL";

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `afterreply` ENUM( '0', '1', '2' ) NOT NULL DEFAULT '0' AFTER `categories`";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

        $update_all_next = 1;
	} // END version 2.0 to 2.1


	// Updating version 2.1 to 2.2
	if ($update_all_next || $hesk_settings['update_from'] == '2.1')
	{
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` (
		  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
		  `from` smallint(5) unsigned NOT NULL,
		  `to` smallint(5) unsigned NOT NULL,
		  `subject` varchar(255) NOT NULL,
		  `message` text NOT NULL,
		  `dt` datetime NOT NULL,
		  `read` enum('0','1') NOT NULL DEFAULT '0',
		  `deletedby` smallint(5) unsigned NOT NULL DEFAULT '0',
		  PRIMARY KEY (`id`),
		  KEY `recipients` (`from`,`to`)
		) ENGINE=MyISAM
		");

		$sql = array();

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `replierid` SMALLINT UNSIGNED NULL AFTER `lastreplier`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `owner` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `status`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `locked` ENUM( '0', '1' ) NOT NULL DEFAULT '0' AFTER `archive`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `history` TEXT NOT NULL AFTER `attachments`";

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` CHANGE `notify` `notify_new_unassigned` ENUM( '0', '1' ) NOT NULL DEFAULT '1'";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `notify_new_my` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `notify_new_unassigned`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `notify_reply_unassigned` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `notify_new_my`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `notify_reply_my` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `notify_reply_unassigned`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `notify_assigned` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `notify_reply_my`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `notify_pm` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `notify_assigned`";

        $sql[] = "UPDATE  `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `categories` = TRIM(TRAILING ',' FROM `categories`)";
        $sql[] = "UPDATE  `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges` = TRIM(TRAILING ',' FROM `heskprivileges`)";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

		// Update privileges - anyone can assign ticket to himself/herself by default
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges`=CONCAT(`heskprivileges`,',can_assign_self') WHERE `isadmin`!='1' ");

        $update_all_next = 1;
	} // END version 2.1 to 2.2


	// Updating version 2.2 to 2.3
	if ($update_all_next || $hesk_settings['update_from'] == '2.2')
	{
    	// Logins table
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` (
		  `ip` varchar(46) NOT NULL,
		  `number` tinyint(3) unsigned NOT NULL DEFAULT '1',
		  `last_attempt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
		  UNIQUE KEY `ip` (`ip`)
		) ENGINE=MyISAM
		");

        // Online table
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."online` (
		  `user_id` smallint(5) unsigned NOT NULL,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
		  `tmp` int(11) unsigned NOT NULL DEFAULT '0',
		  UNIQUE KEY `user_id` (`user_id`),
		  KEY `dt` (`dt`)
		) ENGINE=MyISAM
		");

		$sql = array();

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `trackid` `trackid` VARCHAR( 13 ) NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `priority` `priority` ENUM( '0', '1', '2', '3' ) NOT NULL DEFAULT '3'";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `status` `status` ENUM('0','1','2','3','4','5') NOT NULL DEFAULT '0'";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `ip` `ip` VARCHAR( 46 ) NOT NULL DEFAULT ''";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `lastchange` `lastchange` TIMESTAMP on update CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `email` `email` VARCHAR(255) NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD INDEX (`owner`) ";

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` CHANGE `heskprivileges` `heskprivileges` TEXT NOT NULL";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `autoassign` ENUM('0','1') NOT NULL DEFAULT '1' AFTER `notify_pm`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `default_list` VARCHAR( 255) NOT NULL DEFAULT '' AFTER `notify_pm`";
    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD INDEX (`autoassign`) ";

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` CHANGE `ticket_id` `ticket_id` VARCHAR(13) NOT NULL DEFAULT ''";

    	$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `replyto` `replyto` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0'";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

        // Update staff with new permissions (allowed by default)
		$res = hesk_dbQuery("SELECT `id`,`heskprivileges` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `isadmin` != '1' ");
		while ($row=hesk_dbFetchAssoc($res))
		{
			// Not admin, is user allowed to view tickets?
			if (strpos($row['heskprivileges'], 'can_view_tickets') !== false)
			{
				hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges`=CONCAT(`heskprivileges`,',can_view_unassigned,can_view_online') WHERE `id`=".intval($row['id']));
			}
            else
            {
				hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges`=CONCAT(`heskprivileges`,',can_view_online') WHERE `id`=".intval($row['id']));
            }
		}

        $update_all_next = 1;
	} // END version 2.2 to 2.3


	// Updating version 2.3 to 2.4
	if ($update_all_next || $hesk_settings['update_from'] == '2.3')
	{
    	// Email loops table
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` (
		  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
		  `hits` smallint(1) unsigned NOT NULL DEFAULT '0',
		  `message_hash` char(32) COLLATE utf8_unicode_ci NOT NULL,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
		  KEY `email` (`email`,`hits`)
		) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
		");

		$sql = array();

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_attachments` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."online` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."std_replies` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `language` VARCHAR(50) NULL DEFAULT NULL AFTER `ip`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `merged` MEDIUMTEXT NOT NULL AFTER `attachments`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `time_worked` TIME NOT NULL DEFAULT '00:00:00' AFTER `owner`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `status` `status` ENUM( '0', '1', '2', '3', '4', '5' ) NOT NULL DEFAULT '0'";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `language` VARCHAR(50) NULL DEFAULT NULL AFTER `signature`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `notify_note` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `notify_pm`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `autostart` ENUM( '0', '1' ) NOT NULL DEFAULT '1' AFTER `afterreply`";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` ADD `autoassign` ENUM( '0', '1' ) NOT NULL DEFAULT '1', ADD `type` ENUM( '0', '1' ) NOT NULL DEFAULT '0', ADD INDEX ( `type` )";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` ADD `keywords` MEDIUMTEXT NOT NULL AFTER `content`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` ADD `sticky` ENUM( '0', '1' ) NOT NULL DEFAULT '0' AFTER `html` , ADD INDEX ( `sticky` )";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` DROP INDEX `subject` , ADD FULLTEXT `subject` (`subject` , `content` , `keywords`)";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` ADD `read` ENUM( '0', '1' ) NOT NULL DEFAULT '1'";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `read` `read` ENUM( '0', '1' ) NOT NULL DEFAULT '0'";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

        // Update staff with new permissions (allowed by default)
		$res = hesk_dbQuery("SELECT `id`,`heskprivileges` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `isadmin` != '1' ");
		while ($row=hesk_dbFetchAssoc($res))
		{
			// Not admin, is user allowed to view tickets?
			if (strpos($row['heskprivileges'], 'can_edit_tickets') !== false)
			{
				hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges`=CONCAT(`heskprivileges`,',can_merge_tickets') WHERE `id`=".intval($row['id']));
			}
		}

        $update_all_next = 1;
	} // END version 2.3 to 2.4


    // Upgrade version 2.4.x to 2.5.0
	if ($update_all_next || $hesk_settings['update_from'] == '2.4')
	{
		$sql = array();

		// Make sure the 2.4 to 2.4.1 change is made
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` CHANGE `hits` `hits` SMALLINT( 1 ) UNSIGNED NOT NULL DEFAULT '0' ";

		// 2.4.2 to 2.5.0 specific changes
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` CHANGE `articles` `articles` SMALLINT( 5 ) UNSIGNED NOT NULL DEFAULT '0'";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` ADD `articles_private` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `articles` , ADD `articles_draft` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `articles_private`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` ADD INDEX ( `parent` )";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` DROP INDEX `recipients`";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` ADD INDEX ( `to`, `read`, `deletedby` )";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` ADD INDEX ( `from` )";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `rating` `rating` ENUM( '0', '1', '5' ) DEFAULT '0' ";
		$sql[] = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `rating` = '0' WHERE `rating` IS NULL OR `rating` = '' ";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

        // Update knowledgebase category article counts to reflect new fields
		$update_these = array();

		// Get a count of all articles grouped by category and type
		$res = hesk_dbQuery('SELECT `catid`, `type`, COUNT(*) AS `num` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'kb_articles` GROUP BY `catid`, `type`');
		while ( $row = hesk_dbFetchAssoc($res) )
		{
	    	switch ($row['type'])
	        {
	        	case 0:
	            	$update_these[$row['catid']]['articles'] = $row['num'];
	                break;
	        	case 1:
	            	$update_these[$row['catid']]['articles_private'] = $row['num'];
	                break;
	        	default:
	            	$update_these[$row['catid']]['articles_draft'] = $row['num'];
	        }
		}

	    // Set all article counts to 0
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` SET `articles`=0, `articles_private`=0, `articles_draft`=0");

	    // Now update categories that have articles with correct values
	    foreach ($update_these as $catid => $value)
	    {
	    	$value['articles'] = isset($value['articles']) ? $value['articles'] : 0;
	    	$value['articles_private'] = isset($value['articles_private']) ? $value['articles_private'] : 0;
	    	$value['articles_draft'] = isset($value['articles_draft']) ? $value['articles_draft'] : 0;
			hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` SET `articles`={$value['articles']}, `articles_private`={$value['articles_private']}, `articles_draft`={$value['articles_draft']} WHERE `id`='{$catid}'");

			// Force order articles
			$res = hesk_dbQuery("SELECT `id`, `sticky` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` WHERE `catid`='{$catid}' ORDER BY `sticky` DESC, `art_order` ASC");

			$i = 10;
			$previous_sticky = 1;

			while ( $article = hesk_dbFetchAssoc($res) )
			{
				if ($previous_sticky != $article['sticky'])
				{
					$i = 10;
					$previous_sticky = $article['sticky'];
				}
				hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` SET `art_order`=".intval($i)." WHERE `id`='".intval($article['id'])."'");
				$i += 10;
			}
	    }

		// Force order categories
		$res = hesk_dbQuery('SELECT `id`, `parent` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'kb_categories` ORDER BY `parent` ASC, `cat_order` ASC');
		$i = 10;

		while ( $category = hesk_dbFetchAssoc($res) )
		{
			hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` SET `cat_order`=".intval($i)." WHERE `id`='".intval($category['id'])."'");
			$i += 10;
		}

		$update_all_next = 1;
	} // END version 2.4.0 to 2.5.0

	// 2.5.1 no changes
    // 2.5.2 no changes
    // 2.5.3 no changes
	// 2.5.4 no changes
	// 2.5.5 no changes

	// Upgrade version 2.5.x to 2.6.0
	if ($update_all_next || $hesk_settings['update_from'] == '2.5')
	{
		// -> Banned emails
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."banned_emails` (
		  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
		  `email` varchar(255) NOT NULL,
		  `banned_by` smallint(5) unsigned NOT NULL,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
		  PRIMARY KEY (`id`),
		  KEY `email` (`email`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8
		");

		// -> Banned IPs
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."banned_ips` (
		  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
		  `ip_from` int(10) unsigned NOT NULL DEFAULT '0',
		  `ip_to` int(10) unsigned NOT NULL DEFAULT '0',
		  `ip_display` varchar(100) NOT NULL,
		  `banned_by` smallint(5) unsigned NOT NULL,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
		  PRIMARY KEY (`id`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8
		");

		// -> Reply drafts
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` (
		  `owner` smallint(5) unsigned NOT NULL,
		  `ticket` mediumint(8) unsigned NOT NULL,
		  `message` mediumtext CHARACTER SET utf8 NOT NULL,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
		  KEY `owner` (`owner`),
		  KEY `ticket` (`ticket`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
		");

		// -> Reset password
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` (
		  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
		  `user` smallint(5) unsigned NOT NULL,
		  `hash` char(40) NOT NULL,
		  `ip` varchar(45) NOT NULL,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
		  PRIMARY KEY (`id`),
		  KEY `user` (`user`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
		");

		// -> Service messages
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."service_messages` (
		  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
		  `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
		  `author` smallint(5) unsigned NOT NULL,
		  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
		  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
		  `style` enum('0','1','2','3','4') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
		  `type` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
		  `order` smallint(5) unsigned NOT NULL DEFAULT '0',
		  PRIMARY KEY (`id`),
		  KEY `type` (`type`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
		");

		// -> Ticket templates
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_templates` (
		  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
		  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
		  `message` mediumtext COLLATE utf8_unicode_ci NOT NULL,
		  `tpl_order` smallint(5) unsigned NOT NULL DEFAULT '0',
		  PRIMARY KEY (`id`)
		) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
		");

		// 2.6.0 table changes
		$sql = array();

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` CHANGE `dt` `dt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` CHANGE `dt` `dt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP";
		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `dt` `dt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` ADD `type` ENUM( '0', '1' ) NOT NULL DEFAULT '0'";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` ADD `priority` ENUM( '0', '1', '2', '3' ) NOT NULL DEFAULT '3'";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` CHANGE `type` `type` ENUM('0','1') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '0'";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` CHANGE `ip` `ip` VARCHAR(45) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` ADD `attachments` MEDIUMTEXT NOT NULL";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `rating` `rating` ENUM('1','5') CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, ADD INDEX(`dt`),  ADD INDEX(`staffid`)";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
					CHANGE `email` `email` VARCHAR( 1000 ) NOT NULL DEFAULT '',
					CHANGE `ip` `ip` VARCHAR(45) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
					ADD `firstreply` TIMESTAMP NULL DEFAULT NULL AFTER `lastchange`,
					ADD `closedat` TIMESTAMP NULL DEFAULT NULL AFTER `firstreply`,
					ADD `articles` VARCHAR(255) NULL DEFAULT NULL AFTER `closedat`,
					ADD `openedby` MEDIUMINT(8) DEFAULT '0' AFTER `status`,
					ADD `firstreplyby` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `openedby`,
					ADD `closedby` MEDIUMINT(8) NULL DEFAULT NULL AFTER `firstreplyby`,
					ADD `replies` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0' AFTER `closedby`,
					ADD `staffreplies` SMALLINT( 5 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `replies`,
					ADD INDEX ( `openedby` , `firstreplyby` , `closedby` ),
					ADD INDEX(`dt`)";

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users`
					CHANGE `signature` `signature` VARCHAR( 1000 ) NOT NULL DEFAULT '',
					CHANGE `heskprivileges` `heskprivileges` VARCHAR(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
					CHANGE `categories` `categories` VARCHAR(500) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
					ADD `notify_customer_new` ENUM('0','1') NOT NULL DEFAULT '1' AFTER `autostart`,
					ADD `notify_customer_reply` ENUM('0','1') NOT NULL DEFAULT '1'  AFTER `notify_customer_new`,
					ADD `show_suggested` ENUM('0','1') NOT NULL DEFAULT '1'  AFTER `notify_customer_reply`";

		foreach ($sql as $s)
	    {
			hesk_dbQuery($s);
	    }

		// ==> Populate new fields where available

		// Get list of valid ticket categories
		$cat = array();
		$res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories`");
		while ($row=hesk_dbFetchAssoc($res))
		{
			$cat[]=$row['id'];
		}


		// Update tickets
		$res = hesk_dbQuery("SELECT `id`, `category` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`");
		while ($ticket = hesk_dbFetchAssoc($res) )
		{
			$sql = array();

			// Verify that the category is valid
			if ( ! in_array($ticket['category'], $cat) )
			{
				$sql[] = " `category`=1 ";
			}


			// Update number of staff replies
			$res2 = hesk_dbQuery("SELECT COUNT(*) as `cnt`, (CASE WHEN `staffid` = 0 THEN 0 ELSE 1 END) AS `staffcnt` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`=".intval($ticket['id'])." GROUP BY `staffcnt`");

			$total			= 0;
			$staffreplies	= 0;

			while ( $row = hesk_dbFetchAssoc($res2) )
			{
				$total += $row['cnt'];
				$staffreplies += ($row['staffcnt'] ? $row['cnt'] : 0);
			}

			if ( $total > 0 )
			{
				$sql[] = " `replies`={$total}, `staffreplies`={$staffreplies} ";
			}

			// If we have staff replies, find the first one
			if ( $staffreplies > 0 )
			{
				$res2 = hesk_dbQuery("SELECT `dt`, `staffid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`={$ticket['id']} AND `staffid`>0 ORDER BY `id` ASC LIMIT 1");

				if ( hesk_dbNumRows($res2) == 1)
				{
					$reply = hesk_dbFetchAssoc($res2);
					$sql[] = " `firstreply`='".hesk_dbEscape($reply['dt'])."', `firstreplyby`={$reply['staffid']} ";
				}
			}

			// Do we need to update the ticket?
			if ( count($sql) )
			{
				hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET " . implode(',', $sql) . ", `lastchange`=`lastchange` WHERE `id`={$ticket['id']}");
			}
		}

		$update_all_next = 1;
	} // END version 2.5.x to 2.6.0

	// 2.6.1 no changes

	// 2.6.2 change `closedby` type for all 2.6.x to be sure
	if ($hesk_settings['update_from'] == '2.6')
	{
		hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `closedby` `closedby` MEDIUMINT(8) NULL DEFAULT NULL");
	}

	// 2.6.3 no changes
	// 2.6.4 no changes
	// 2.6.5 no changes
	// 2.6.6 no changes
	// 2.6.7 no changes

	// Updating version 2.6 to 2.7
	if ($update_all_next || $hesk_settings['update_from'] == '2.6')
	{
        // Delete old export folder
        $export_dir = HESK_PATH.$hesk_settings['attach_dir'].'/export/';
        if (is_dir($export_dir))
        {
            $files = glob($export_dir.'*', GLOB_NOSORT);
            if (is_array($files))
            {
                array_walk($files, 'hesk_unlink_callable');
            }
            @rmdir($export_dir);
        }

        // Delete old __latest.txt file
        hesk_unlink(HESK_PATH.$hesk_settings['attach_dir'].'/__latest.txt');

        // Delete HTMLPurifier cache
        if (is_dir(HESK_PATH.'inc/htmlpurifier/standalone/HTMLPurifier/DefinitionCache/Serializer'))
        {
            hesk_rrmdir(HESK_PATH.'inc/htmlpurifier/standalone/HTMLPurifier/DefinitionCache/Serializer', true);
        }

		$sql = array();

		$sql[] = "ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `autoreload` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `autostart`";

		foreach ($sql as $s)
		{
			hesk_dbQuery($s);
		}

		// Add new custom field columns and make sure all are set to mediumtext
		$sql = array();
		$table = array();

		// Is the status column an enum type? (HESK < 2.7.0) If no, don't subtract 1 later on
		$res = hesk_dbQuery("SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '".hesk_dbEscape($hesk_settings['db_name'])."' AND TABLE_NAME = '".hesk_dbEscape($hesk_settings['db_pfix'])."tickets' AND COLUMN_NAME = 'status'");
		$adjust_status_column = false;
		if (hesk_dbResult($res) == 'enum') {
			$adjust_status_column = true;
		}
		// Change the type regardless
		$sql[] = "CHANGE `status` `status` TINYINT UNSIGNED NOT NULL DEFAULT '0'";

		$res = hesk_dbQuery("DESCRIBE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`");
		while($row = hesk_dbFetchAssoc($res))
		{
			$table[$row['Field']] = $row['Type'];
		}

		for ($i=1; $i<=50; $i++)
		{
			if (isset($table['custom'.$i]))
			{
                if (strtolower($table['custom'.$i]) != 'mediumtext')
                {
		            $sql[] = 'CHANGE `custom'.$i.'` `custom'.$i.'` MEDIUMTEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL';
                }
			}
			else
			{
		    	$sql[] = 'ADD `custom'.$i.'` MEDIUMTEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL';
			}
		}

		hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` " . implode(',', $sql));

		// A tweak to fix converting enum to int
		if ($adjust_status_column) {
			hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `status`=`status`-1, `lastchange`=`lastchange`");
		}

		// -> Custom fields
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` (
		  `id` tinyint(3) UNSIGNED NOT NULL,
		  `use` enum('0','1','2') NOT NULL DEFAULT '0',
		  `place` enum('0','1') NOT NULL DEFAULT '0',
		  `type` varchar(20) NOT NULL DEFAULT 'text',
		  `req` enum('0','1','2') NOT NULL DEFAULT '0',
		  `category` text,
		  `name` text,
		  `value` text,
		  `order` smallint(5) UNSIGNED NOT NULL DEFAULT '10',
		  PRIMARY KEY (`id`),
		  KEY `useType` (`use`,`type`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
		");

		// ---> Insert empty custom fields
		hesk_dbQuery("
		INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` (`id`, `use`, `place`, `type`, `req`, `category`, `name`, `value`, `order`) VALUES
		(1, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(2, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(3, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(4, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(5, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(6, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(7, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(8, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(9, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(10, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(11, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(12, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(13, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(14, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(15, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(16, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(17, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(18, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(19, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(20, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(21, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(22, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(23, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(24, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(25, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(26, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(27, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(28, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(29, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(30, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(31, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(32, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(33, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(34, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(35, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(36, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(37, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(38, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(39, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(40, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(41, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(42, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(43, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(44, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(45, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(46, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(47, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(48, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(49, '0', '0', 'text', '0', NULL, '', NULL, 1000),
		(50, '0', '0', 'text', '0', NULL, '', NULL, 1000)
		");

		// ---> Update custom fields with current values
		if (isset($hesk_settings['custom_fields']))
		{
			foreach ($hesk_settings['custom_fields'] as $k => $v)
			{
				if ( ! $v['use'])
				{
			    	continue;
				}

				$cf = array();

				// ID
				$id = intval(str_replace('custom', '', $k));

				// Name
				$cf['names'][$hesk_settings['language']] = $v['name'];
				$cf['names'] = addslashes(json_encode($cf['names']));

				// Type and value
				$cf['type'] = $v['type'];
				switch ($cf['type'])
				{
					case 'textarea':
						$size = explode('#',$v['value']);
						$cf['rows'] = empty($size[0]) ? 5 : intval($size[0]);
						$cf['cols'] = empty($size[1]) ? 30 : intval($size[1]);
						$cf['value'] = array('rows' => $cf['rows'], 'cols' => $cf['cols']);
						break;

					case 'radio':
						$options = explode('#HESK#',$v['value']);
						$cf['value'] = array('radio_options' => $options);
						break;

					case 'select':
						$v['value'] = str_replace('{HESK_SELECT}', '', $v['value'], $num);
						$options = explode('#HESK#',$v['value']);
						$cf['value'] = array('show_select' => ($num ? 1 : 0), 'select_options' => $options);
						break;

					case 'checkbox':
						$options = explode('#HESK#',$v['value']);
						$cf['value'] = array('checkbox_options' => $options);
						break;

					default:
						$cf['type'] = 'text';
						$cf['max_length'] = intval($v['maxlen']);
						$cf['default_value'] = $v['value'];
						$cf['value'] = array('max_length' => $cf['max_length'], 'default_value' => $cf['default_value']);
				}

				$cf['value'] = addslashes(json_encode($cf['value']));

				// Update custom_fields table with this field settings
				hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` SET
				`use`      = '1',
				`place`    = '".($v['place'] ? '1' : '0')."',
				`type`     = '{$cf['type']}',
				`req`      = '".($v['req'] ? '1' : '0')."',
				`name`     = '".hesk_dbEscape($cf['names'])."',
				`value`    = ".(strlen($cf['value']) ? "'".hesk_dbEscape($cf['value'])."'" : 'NULL')."
				WHERE `id`={$id}");
			}
		}

		// -> Custom statuses
		hesk_dbQuery("
		CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_statuses` (
		`id` tinyint(3) UNSIGNED NOT NULL,
		`name` text NOT NULL,
		`color` varchar(6) NOT NULL,
		`can_customers_change` enum('0','1') NOT NULL DEFAULT '1',
		`order` smallint(5) UNSIGNED NOT NULL DEFAULT '10',
		PRIMARY KEY (`id`)
		) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
		");

		// Update staff with new permissions (allowed by default)
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges`=CONCAT(`heskprivileges`,',can_resolve,can_submit_any_cat') WHERE `isadmin` = '0' AND `heskprivileges` LIKE '%can_reply_tickets%' ");

		$update_all_next = 1;
	} // END version 2.6 to 2.7

	// Updating version 2.7 to 2.8
	if ($update_all_next || $hesk_settings['update_from'] == '2.7')
	{
    	// Modify tickets table
		hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD `assignedby` MEDIUMINT NULL DEFAULT NULL AFTER `owner`, ADD INDEX (`assignedby`)");

		$update_all_next = 1;
    } // END version 2.7 to 2.8

    // Updating 2.8 to 2.8.2
    if ($update_all_next || $hesk_settings['update_from'] == '2.8')
    {
        // Modify service_messages table
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."service_messages` ADD `language` VARCHAR(50) NULL DEFAULT NULL AFTER `message`");

        $update_all_next = 1;
    } // END version 2.8 to 2.8.2

    // Updating 2.8.2 to 2.8.3
    if ($update_all_next || $hesk_settings['update_from'] == '2.8.2')
    {
        // Modify tickets table
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `name` `name` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '', CHANGE `subject` `subject` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT ''");

        $update_all_next = 1;
    } // END version 2.8.2 to 2.8.3

    // 2.8.4 no changes
    // 2.8.5 no changes
    // 3.0.0 no changes
    // 3.0.1 no changes
    // 3.0.2 no changes
    // 3.0.3 no changes
    // 3.1.0 no changes
    // 3.1.1 no changes
    // 3.1.2 no changes

    // Updating 2.8.3 through 3.1.1 to 3.2.0
    if ($update_all_next || $hesk_settings['update_from'] == '2.8.3') {
        // Modify tickets table with due date-related fields only if it doesn't exist (MfH users will already have these columns)
        $existing_column_rs = hesk_dbQuery("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
            WHERE table_name = '".hesk_dbEscape($hesk_settings['db_pfix'])."tickets'
            AND table_schema = '".hesk_dbEscape($hesk_settings['db_name'])."'
            AND column_name = 'due_date'");

        if (hesk_dbNumRows($existing_column_rs) === 0) {
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD COLUMN `message_html` mediumtext DEFAULT NULL AFTER `message`, ADD COLUMN `due_date` timestamp NULL DEFAULT NULL, ADD COLUMN `overdue_email_sent` tinyint(1) DEFAULT '0'");
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD COLUMN `notify_overdue_unassigned` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1' AFTER `notify_note`");
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD COLUMN `notify_overdue_my` enum('0','1') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1' AFTER `notify_overdue_unassigned`");
        } else {
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD COLUMN `message_html` mediumtext DEFAULT NULL AFTER `message`");
        }

        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `message_html` = `message`, `lastchange`=`lastchange`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `name` `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', ADD COLUMN `message_html` mediumtext DEFAULT NULL AFTER `message`");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `message_html` = `message`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` ADD COLUMN `message_html` mediumtext COLLATE utf8_unicode_ci NULL AFTER `message`");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` SET `message_html` = `message`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."std_replies` ADD COLUMN `message_html` mediumtext COLLATE utf8_unicode_ci NULL AFTER `message`");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."std_replies` SET `message_html` = `message`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_templates` ADD COLUMN `message_html` mediumtext COLLATE utf8_unicode_ci NULL AFTER `message`");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_templates` SET `message_html` = `message`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` CHANGE `name` `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ''");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` CHANGE `name` `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ''");

        // -> Log of overdue tickets
        hesk_dbQuery("
        CREATE TABLE IF NOT EXISTS `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` (
          `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
          `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          `ticket` mediumint(8) UNSIGNED NOT NULL,
          `category` smallint(5) UNSIGNED NOT NULL,
          `priority` enum('0','1','2','3') NOT NULL,
          `status` tinyint(3) UNSIGNED NOT NULL,
          `owner` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
          `due_date` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
          `comments` varchar(255) DEFAULT NULL,
          PRIMARY KEY (`id`),
          KEY `ticket` (`ticket`),
          KEY `category` (`category`),
          KEY `priority` (`priority`),
          KEY `status` (`status`),
          KEY `owner` (`owner`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
        ");

        $update_all_next = 1;
    }

    // Updating 3.2.0 and 3.2.1 to 3.2.2
    if ($update_all_next || $hesk_settings['update_from'] == '3.2.0') {
        // Fix any missing HTML messages from merged tickets
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `message_html` = `message` WHERE `message_html` IS NULL");
        $update_all_next = 1;
    }

    // 3.2.3 no changes
    // 3.2.4 no changes
    // 3.2.5 no changes

    // Updating 3.2.x to 3.3.0
    if ($update_all_next || $hesk_settings['update_from'] == '3.2.0') {
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users`
                    CHANGE `user` `user` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
                    CHANGE `pass` `pass` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT ''
        ");

        // -> Authentication tokens
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` (
          `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
          `selector` char(12) DEFAULT NULL,
          `token` char(64) DEFAULT NULL,
          `user_id` smallint(5) UNSIGNED NOT NULL,
          `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          `expires` timestamp NULL DEFAULT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
        ");

        // Category-default due dates
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories`
                    ADD `autoassign_config` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL AFTER `autoassign`,
                    ADD `default_due_date_amount` int(11) DEFAULT NULL AFTER `priority`,
                    ADD `default_due_date_unit` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL AFTER `default_due_date_amount`
        ");

        // MFA
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users`
                    ADD `mfa_enrollment` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `replies`,
                    ADD `mfa_secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL AFTER `mfa_enrollment`
        ");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `user_id` smallint(5) UNSIGNED NOT NULL,
          `verification_token` varchar(255) NOT NULL,
          `expires_at` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
          PRIMARY KEY (`id`),
          KEY `user_id` (`user_id`),
          KEY `verification_token` (`verification_token`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
        ");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          `user_id` smallint(5) UNSIGNED NOT NULL,
          `code` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
        ");

        // Add id columns
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."online` ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)");

        // Ticket satisfaction - two steps, to ignore all existing tickets
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
                    ADD `satisfaction_email_sent` TINYINT(1) DEFAULT '1' AFTER `overdue_email_sent`,
                    ADD `satisfaction_email_dt` TIMESTAMP NULL DEFAULT NULL AFTER `satisfaction_email_sent`
        ");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `satisfaction_email_sent` `satisfaction_email_sent` TINYINT(1) DEFAULT '0'");

        // -> Temporary Attachments (Drag & Drop)
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments` (
            `att_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
            `unique_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
            `saved_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
            `real_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
            `size` int(10) unsigned NOT NULL DEFAULT '0',
            `expires_at` timestamp NOT NULL,
            PRIMARY KEY (`att_id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
        ");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments_limits` (
            `ip` varchar(45) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
            `upload_count` int(10) unsigned NOT NULL DEFAULT 1,
            `last_upload_at` timestamp NOT NULL DEFAULT NOW(),
            PRIMARY KEY (`ip`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
        ");

        $update_all_next = 1;
    }

    // copy new_ticket.txt into new_ticket_by_staff.txt
    $eml_fp = HESK_PATH . 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/emails/';
    if (file_exists($eml_fp . 'new_ticket.txt') &&
        file_exists($eml_fp . 'new_ticket_by_staff.txt') &&
        is_writable($eml_fp . 'new_ticket_by_staff.txt')
    ) {
        $eml = file_get_contents($eml_fp . 'new_ticket.txt');
        file_put_contents($eml_fp . 'new_ticket_by_staff.txt', $eml);
    }

    // 3.3.1 no changes
    // 3.3.2 no changes

    // Updating 3.3.x to 3.4.0
    if ($update_all_next || $hesk_settings['update_from'] == '3.3.0') {
        // -> OAuth tokens
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_providers` (
          `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
          `name` varchar(255) NOT NULL,
          `authorization_url` text NOT NULL,
          `token_url` text NOT NULL,
          `client_id` text NOT NULL,
          `client_secret` text NOT NULL,
          `scope` text NOT NULL,
          `no_val_ssl` tinyint(4) NOT NULL DEFAULT '0',
          `verified` smallint NOT NULL DEFAULT 0,
          PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
        ");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_tokens` (
          `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
          `provider_id` int(11) NOT NULL,
          `token_value` text DEFAULT NULL,
          `token_type` varchar(32) NOT NULL,
          `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          `expires` timestamp NULL DEFAULT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
        ");

        // Add the new 'can_due_date' permission to existing users who had that permission
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `heskprivileges`=CONCAT(`heskprivileges`,',can_due_date') WHERE `isadmin` = '0' AND `heskprivileges` REGEXP 'can_reply_tickets|can_edit_tickets' ");

        $update_all_next = 1;
    }

    // 3.4.1 no changes
    // 3.4.2 no changes
    // 3.4.3 no changes
    // 3.4.4 no changes
    // 3.4.5 no changes
    // 3.4.6 no changes

    // Updating 3.4.x to 3.5.0
    if ($update_all_next || $hesk_settings['update_from'] == '3.4.0') {
        // -> Customer Accounts
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (
          `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
          `pass` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
          `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
          `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
          `language` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
          `verified` smallint(1) UNSIGNED NOT NULL DEFAULT 0,
          `verification_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
          `verification_email_sent_at` timestamp NULL,
          `mfa_enrollment` smallint(5) UNSIGNED NOT NULL DEFAULT 0,
          `mfa_secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
          PRIMARY KEY (`id`),
          KEY `email` (`email`)
        ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes` (
          `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
          `customer_id` mediumint(8) unsigned NOT NULL,
          `new_email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
          `verification_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
          `expires_at` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
          PRIMARY KEY (`id`),
          KEY `email` (`new_email`)
        ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (
          `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
          `ticket_id` mediumint(8) unsigned NOT NULL,
          `customer_id` mediumint(8) unsigned NOT NULL,
          `customer_type` varchar(9) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'REQUESTER',
          PRIMARY KEY (`id`),
          KEY `ticket_id` (`ticket_id`),
          KEY `customer_id` (`customer_id`)
        ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");
        // -> Bookmarks
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."bookmarks` (
          `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
          `user_id` smallint(5) unsigned NOT NULL,
          `ticket_id` mediumint(8) unsigned NOT NULL,
          PRIMARY KEY (`id`),
          KEY `ticket_id` (`ticket_id`,`user_id`),
          KEY `user_id` (`user_id`,`ticket_id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
        ");

        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
                    ADD `eid` varchar(1000) NULL DEFAULT NULL AFTER `satisfaction_email_dt`,
                    CHANGE `name` `u_name` varchar(255) NOT NULL default '',
                    CHANGE `email` `u_email` varchar(1000) NOT NULL default ''");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies`
                    ADD `customer_id` mediumint(8) unsigned NULL DEFAULT NULL AFTER `staffid`,
                    ADD `eid` varchar(1000) NULL DEFAULT NULL AFTER `read`;");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password`
                    ADD `user_type` varchar(8) NOT NULL DEFAULT 'STAFF' AFTER `dt`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens`
                    ADD `user_type` varchar(8) NOT NULL DEFAULT 'STAFF' AFTER `user_id`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes`
                    ADD `user_type` varchar(8) NOT NULL DEFAULT 'STAFF' AFTER `user_id`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens`
                    ADD `user_type` varchar(8) NOT NULL DEFAULT 'STAFF' AFTER `user_id`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."service_messages`
                    ADD `location` varchar(255) NULL DEFAULT NULL AFTER `order`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users`
                    ADD `notify_customer_approval` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `notify_overdue_my`,
                    CHANGE `default_list` `default_list` VARCHAR(1000) NOT NULL DEFAULT ''");
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_rejections` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
          PRIMARY KEY (`id`),
          KEY `email` (`email`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        // Increase custom fields to 100; try preserving current custom fields
        $sql = array();
        $sql_custom = array();
        $res = hesk_dbQuery("DESCRIBE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`");
        while($row = hesk_dbFetchAssoc($res)) {
            $table[$row['Field']] = $row['Type'];
        }
        for ($i=51; $i<=100; $i++) {
            if (isset($table['custom'.$i])) {
                if (strtolower($table['custom'.$i]) != 'mediumtext') {
                    $sql[] = 'CHANGE `custom'.$i.'` `custom'.$i.'` MEDIUMTEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL';
                }
            } else {
                $sql[] = 'ADD `custom'.$i.'` MEDIUMTEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL';
                $sql_custom[] = "({$i}, '0', '0', 'text', '0', NULL, '', NULL, 1000)";
            }
        }
        if (count($sql)) {
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` " . implode(',', $sql));
        }
        if (count($sql_custom)) {
            hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` (`id`, `use`, `place`, `type`, `req`, `category`, `name`, `value`, `order`) VALUES ".implode(',', $sql_custom)."");
        }

        $update_all_next = 1;
    }

    // 3.5.1 no changes

    // Updating 3.5.x to 3.5.2
    if ($update_all_next || $hesk_settings['update_from'] == '3.5.0') {
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` CHANGE `eid` `eid` VARCHAR(1000) NULL DEFAULT NULL, ADD KEY `customer_id` (`customer_id`)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE `eid` `eid` VARCHAR(1000) NULL DEFAULT NULL");

        $update_all_next = 1;
    }

    // 3.5.3 no changes

    // Updating 3.5.3 to 3.6.0
    if ($update_all_next || $hesk_settings['update_from'] == '3.5.2') {
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator` (
          `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
          `ticket_id` mediumint UNSIGNED NOT NULL,
          `user_id` smallint UNSIGNED NOT NULL,
          PRIMARY KEY (`id`),
          KEY `ticket_id` (`ticket_id`),
          KEY `user_id` (`user_id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
        ");

        hesk_dbQuery("
        ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users`
        ADD `notify_collaborator_added` ENUM('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '1' AFTER `notify_customer_approval`,
        ADD `notify_collaborator_customer_reply` ENUM('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '1' AFTER `notify_collaborator_added`,
        ADD `notify_collaborator_staff_reply` ENUM('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '0' AFTER `notify_collaborator_customer_reply`,
        ADD `notify_collaborator_note` ENUM('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '1' AFTER `notify_collaborator_staff_reply`,
        ADD `notify_collaborator_resolved` ENUM('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '0' AFTER `notify_collaborator_note`,
        ADD `notify_collaborator_overdue` ENUM('0','1') COLLATE utf8mb3_unicode_ci NOT NULL DEFAULT '1' AFTER `notify_collaborator_resolved`
        ");

        // -> Linked Tickets Table
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."linked_tickets` (
          `id` mediumint(8) NOT NULL AUTO_INCREMENT,
          `ticket_id1` mediumint(8) NOT NULL,
          `ticket_id2` mediumint(8) NOT NULL,
          `dt_created` datetime NOT NULL,
          PRIMARY KEY (`id`),
          KEY `ticket_id1` (`ticket_id1`),
          KEY `ticket_id2` (`ticket_id2`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        // -> Custom Priorities
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` (
          `id` tinyint(3) UNSIGNED NOT NULL,
          `name` text NOT NULL,
          `color` varchar(6) NOT NULL,
          `can_customers_select` enum('0','1') NOT NULL DEFAULT '1',
          `priority_order` smallint(5) UNSIGNED NOT NULL DEFAULT '10',
          PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        hesk_dbQuery("
        INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` (`id`, `name`, `color`, `can_customers_select`, `priority_order`) VALUES
        (0, '{\"English\":\"NULL\"}', 'e74441', '0', 4),
        (1, '{\"English\":\"NULL\"}', 'fac500', '1', 3),
        (2, '{\"English\":\"NULL\"}', '3abb7a', '1', 2),
        (3, '{\"English\":\"NULL\"}', '71a5ec', '1', 1)
        ");

        //Change categories table priority column datatype
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` ADD COLUMN `old_priority` tinyint(3)");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories`
            SET `old_priority` =
                CASE `priority`
                    WHEN '0' THEN 0
                    WHEN '1' THEN 1
                    WHEN '2' THEN 2
                    WHEN '3' THEN 3
                END
        ");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` DROP COLUMN `priority`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` CHANGE  `old_priority` `priority` tinyint(3)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` MODIFY `priority` tinyint(3) UNSIGNED NOT NULL DEFAULT '3' AFTER `type`");

        //Change tickets table priority column datatype
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ADD COLUMN `old_priority` tinyint(3)");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
            SET `old_priority` =
                CASE `priority`
                    WHEN '0' THEN 0
                    WHEN '1' THEN 1
                    WHEN '2' THEN 2
                    WHEN '3' THEN 3
                END,
            `lastchange`=`lastchange`
        ");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` DROP COLUMN `priority`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` CHANGE  `old_priority` `priority` tinyint(3)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` MODIFY `priority` tinyint(3) UNSIGNED NOT NULL DEFAULT '3' AFTER `category`");

        //Change log_overdue table priority column datatype
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` ADD COLUMN `old_priority` tinyint(3)");
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue`
            SET `old_priority` =
                CASE `priority`
                    WHEN '0' THEN 0
                    WHEN '1' THEN 1
                    WHEN '2' THEN 2
                    WHEN '3' THEN 3
                END
        ");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` DROP INDEX `priority`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` DROP COLUMN `priority`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` CHANGE  `old_priority` `priority` tinyint(3)");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` MODIFY `priority` tinyint(3) UNSIGNED NOT NULL AFTER `category`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."log_overdue` ADD INDEX(`priority`)");

        $update_all_next = 1;
    }

    // 3.6.1 no changes
    // 3.6.2 no changes
    // 3.6.3 no changes
    // 3.6.4 no changes

    // Updating 3.6.0 to 3.7.0
    if ($update_all_next || $hesk_settings['update_from'] == '3.6.0') {
        //-> Muted emails
        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."muted_emails` (
          `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
          `email` varchar(255) NOT NULL,
          `type` tinyint UNSIGNED NOT NULL DEFAULT '1',
          `muted_by` smallint(5) unsigned NOT NULL,
          `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          PRIMARY KEY (`id`),
          KEY `email` (`email`),
          KEY `type` (`type`)
        ) ENGINE=MyISAM  DEFAULT CHARSET=utf8
        ");

        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_groups` (
            `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
            `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
            PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories` (
            `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
            `group_id` mediumint(8) unsigned NOT NULL,
            `category_id` smallint(5) unsigned NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `group_id_category_id` (`group_id`, `category_id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features` (
            `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
            `group_id` mediumint(8) unsigned NOT NULL,
            `feature` varchar(255) NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `group_id_feature` (`group_id`, `feature`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` (
            `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
            `group_id` mediumint(8) unsigned NOT NULL,
            `user_id` smallint(5) unsigned NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `group_id_user_id` (`group_id`, `user_id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `nickname` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' AFTER `email`");

        // Check if there is already an "active" column in the users table from legacy "Mods for Hesk"
        $res = hesk_dbQuery("SHOW COLUMNS FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` LIKE 'active'");
        if (hesk_dbNumRows($res)) {
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD COLUMN `old_active` SMALLINT");
            hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users`
                SET `old_active` =
                    CASE `active`
                        WHEN '0' THEN 0
                        WHEN '1' THEN 1
                    END
            ");
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` DROP COLUMN `active`");
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` CHANGE  `old_active` `active` SMALLINT");
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` MODIFY `active` SMALLINT NOT NULL DEFAULT 1 AFTER `isadmin`");
        } else {
            hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ADD `active` SMALLINT NOT NULL DEFAULT 1 AFTER `isadmin`");
        }

        hesk_dbQuery("
        CREATE TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (
            `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
            `email_id` varchar(1000) NOT NULL,
            `ticket_id` mediumint UNSIGNED NOT NULL,
            `reply_id` mediumint UNSIGNED DEFAULT NULL,
            `from_hesk` tinyint UNSIGNED NOT NULL DEFAULT '1',
            PRIMARY KEY (`id`),
            KEY `email_id` (`email_id`(100))
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
        ");

        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (`email_id`, `ticket_id`, `from_hesk`) SELECT `eid`, `id`, 0 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `eid` IS NOT NULL");
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (`email_id`, `ticket_id`, `reply_id`, `from_hesk`) SELECT `eid`, `replyto`, `id`, 0 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `eid` IS NOT NULL");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` DROP `eid`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` DROP `eid`");

        $update_all_next = 1;
    }

    // 3.7.1 no changes

    // 3.7.0 and 3.7.1 to 3.7.2 make sure that the nickname column has a default value
    if ($hesk_settings['update_from'] == '3.7.0') {
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` CHANGE `nickname` `nickname` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '';");
    }

    // 3.7.3 no changes
    // 3.7.4 no changes
    // 3.7.5 no changes
    // 3.7.6 no changes
    // 3.7.7 no changes

    // 3.7.8 Store only hashed password reset tokens; existing reset links are no longer usable
    if ($update_all_next || $hesk_settings['update_from'] == '3.7.0') {
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password`");
        hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` CHANGE `hash` `hash` CHAR(64) NOT NULL, ADD KEY `hash_user_type` (`hash`, `user_type`)");
    }

    // 3.7.9 no changes
    // 3.7.10 no changes

	// Insert the "HESK updated to latest version" mail for the administrator
    $offer_license = file_exists(HESK_PATH.'hesk_license.php') ? "" : "<h3>&raquo; Look professional</h3>\r\n\r\n<p><a href=\"https://www.hesk.com/get/hesk3-license\">Remove &quot;Powered by&quot; links</a> to support Hesk development and make it look more professional.</p>\r\n\r\n";
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` (`id`, `from`, `to`, `subject`, `message`, `dt`, `read`, `deletedby`) VALUES (NULL, 9999, 1, 'Hesk updated to version ".HESK_NEW_VERSION."', '".hesk_dbEscape("</p><div style=\"text-align:justify; padding-left: 10px; padding-right: 10px;\">\r\n\r\n<h2 style=\"padding-left:0px\">Congratulations, your Hesk has been successfully updated! Now is your chance to:</h2>\r\n\r\n<h3>&raquo; Rate us</h3>\r\n\r\n<p>Positive ratings and reviews motivate us to continue developing Hesk. Please take a moment to:</p>\r\n\r\n<ul>\r\n<li>rate or review Hesk at <a href=\"https://softaculous.com/rate/HESK\" rel=\"nofollow\">Softaculous</a></li>\r\n<li>rate or review Hesk at <a href=\"https://alternativeto.net/software/hesk/about/\" rel=\"nofollow\">AlternativeTo</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Stay updated</h3>\r\n\r\n<p>Hesk regularly receives improvements and bug fixes; make sure you know about them!</p>\r\n<ul>\r\n<li>for fast notifications, <a href=\"https://x.com/HESKdotCOM\" rel=\"nofollow\">follow Hesk on <b>X</b></a></li>\r\n<li>for email notifications, subscribe to our low-volume, zero-spam <a href=\"https://www.hesk.com/newsletter.php\">newsletter</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Look professional</h3>\r\n\r\n<p><a href=\"https://www.hesk.com/get/hesk3-license\">Remove &quot;Powered by&quot; links</a> to support Hesk development and make it look more professional.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Tired of manual updates? Upgrade to Hesk Cloud!</h3>\r\n\r\n<p>Experience the best of Hesk by moving your help desk into the Hesk Cloud:</p>\r\n<ul>\r\n<li>exclusive advanced modules,</li>\r\n<li>automated updates,</li>\r\n<li>free migration of your existing Hesk tickets and settings,</li>\r\n<li>we take care of maintenance, server setup and optimization, backups, and more!</li>\r\n</ul>\r\n\r\n<p>&nbsp;<br><a href=\"https://www.hesk.com/get/hesk3-cloud\" class=\"btn btn--blue-border\" style=\"text-decoration:none\">Click here to learn more about Hesk Cloud</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>Best regards,</p>\r\n\r\n<p>Klemen Stirn<br>\r\nFounder<br>\r\n<a href=\"https://www.hesk.com\">https://www.hesk.com</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n</div><p>")."', NOW(), '0', 9999)");

	return true;

} // End hesk_iUpdateTables()


function hesk_iSaveSettings()
{
    global $hesk_settings, $hesklang;

    // Get default settings
	$hesk_default = hesk_defaultSettings();

    // Set a new version number
    $hesk_settings['hesk_version'] = HESK_NEW_VERSION;

	// Correct typos in variable names before 2.4
	if ( isset($hesk_settings['stmp_host_port']) )
	{
		$hesk_settings['smtp_host_port'] = isset($hesk_settings['stmp_host_port']) ? $hesk_settings['stmp_host_port'] : 25;
		$hesk_settings['smtp_timeout']   = isset($hesk_settings['stmp_timeout']) ? $hesk_settings['stmp_timeout'] : 10;
		$hesk_settings['smtp_user']      = isset($hesk_settings['stmp_user']) ? $hesk_settings['stmp_user'] : '';
		$hesk_settings['smtp_password']  = isset($hesk_settings['stmp_password']) ? $hesk_settings['stmp_password'] : '';
	}

    // Try to convert Hesk < 3.3.0 timestamp to 3.3.0 format
    if (isset($hesk_settings['timeformat'])) {
        $hesk_settings['format_timestamp'] = $hesk_settings['timeformat'];
        if ( ! isset($hesk_settings['format_time']) && isset($hesk_settings['timeformat']) && substr_count($hesk_settings['timeformat'], ' ') === 1) {
            list($hesk_settings['format_date'], $hesk_settings['format_time']) = explode(' ', $hesk_settings['timeformat']);
        }
    }

    // Convert Hesk < 3.3.2 settings
    if ( ! isset($hesk_settings['smtp_enc'])) {
        if ($hesk_settings['smtp_tls']) {
            $hesk_settings['smtp_enc'] = 'tls';
        } elseif ($hesk_settings['smtp_ssl'] || stripos($hesk_settings['smtp_host_name'], 'ssl://') === 0) {
            $hesk_settings['smtp_enc'] = 'ssl';
        } else {
            $hesk_settings['smtp_enc'] = '';
        }
    }

    // Fix wrongly saved host name
    if (stripos($hesk_settings['smtp_host_name'], 'ssl://') === 0) {
        $hesk_settings['smtp_host_name'] = substr($hesk_settings['smtp_host_name'], 6);
        if ($hesk_settings['smtp_enc'] == '') {
            $hesk_settings['smtp_enc'] = 'ssl';
        }
    }

	// Assign all required values
    foreach ($hesk_default as $k => $v)
    {
        if (in_array($k, array('language', 'languages'))) {
            continue;
        }
    	if (is_array($v))
        {
        	// Arrays will be processed separately
        	continue;
        }
    	if ( ! isset($hesk_settings[$k]))
        {
			$hesk_settings[$k] = $v;
        }
    }

	// Arrays need special care
    $hesk_settings['barcode'] = isset($hesk_settings['barcode']) ? $hesk_settings['barcode'] : $hesk_default['barcode'];
    $hesk_settings['attachments'] = isset($hesk_settings['attachments']) ? $hesk_settings['attachments'] : $hesk_default['attachments'];
    $hesk_settings['email_providers'] = isset($hesk_settings['email_providers']) ? array_unique(array_merge($hesk_settings['email_providers'], $hesk_default['email_providers'])) : $hesk_default['email_providers'];
    $hesk_settings['notify_spam_tags'] = isset($hesk_settings['notify_spam_tags']) ? $hesk_settings['notify_spam_tags'] : $hesk_default['notify_spam_tags'];
    $hesk_settings['ticket_list'] = isset($hesk_settings['ticket_list']) ? $hesk_settings['ticket_list'] : $hesk_default['ticket_list'];
    $hesk_settings['customer_ticket_list'] = isset($hesk_settings['customer_ticket_list']) ? $hesk_settings['customer_ticket_list'] : $hesk_default['customer_ticket_list'];
    $hesk_settings['theme_overrides'] = isset($hesk_settings['theme_overrides']) ? $hesk_settings['theme_overrides'] : $hesk_default['theme_overrides'];

    // Attachments max size must be multiplied by 1024 since version 2.4
    if ($hesk_settings['attachments']['max_size'] < 102400)
    {
		$hesk_settings['attachments']['max_size'] = $hesk_settings['attachments']['max_size'] * 1024;
    }

    // Encode and escape characters
    $set = $hesk_settings;
    foreach ($hesk_settings as $k=> $v)
    {
    	if (is_array($v) || is_object($v))
        {
        	continue;
        }
    	$set[$k] = addslashes($v);
    }
    $set['debug_mode'] = 0;

    $set['email_providers'] = count($hesk_settings['email_providers']) ?  "'" . implode("','", $hesk_settings['email_providers']) . "'" : '';
    $set['notify_spam_tags'] = count($hesk_settings['notify_spam_tags']) ?  "'" . implode("','", $hesk_settings['notify_spam_tags']) . "'" : '';
    $set['ip_whois'] = str_replace('http://whois.domaintools.com', 'https://whois.domaintools.com', $set['ip_whois']);

    // reCaptcha v1 has been removed in 2.8, disable it
    if ($set['recaptcha_use'] == 1 && version_compare($hesk_settings['update_from'], '2.8', '<'))
    {
        $set['recaptcha_use'] = 0;
        define('RECAPTCHA_V1', true);
    }

    // Some installations have smtp_timeout erroneously set to 0. Ensure it is at least 5.
    $set['smtp_timeout'] = $set['smtp_timeout'] > 5 ? $set['smtp_timeout'] : 10;

	hesk_iSaveSettingsFile($set);

	return true;

} // End hesk_iSaveSettings()


function hesk_defaultSettings()
{
	$spam_question = hesk_generate_SPAM_question();

	$secimg_sum = '';
	for ($i=1;$i<=10;$i++)
	{
	    $secimg_sum .= substr('AEUYBDGHJLMNPQRSTVWXZ123456789', rand(0,29), 1);
	}

	// --> General settings
	$hesk_settings['site_title']='Website';
	$hesk_settings['site_url']='https://www.example.com';
	$hesk_settings['hesk_title']='Help Desk';
	$hesk_settings['hesk_url']='https://www.example.com/helpdesk';
	$hesk_settings['webmaster_mail']='support@example.com';
    $hesk_settings['site_theme']='hesk3';
    $hesk_settings['admin_css']=0;
    $hesk_settings['admin_css_url']='https://www.example.com/hesk-style.css';
    $hesk_settings['admin_js']=0;
    $hesk_settings['admin_js_url']='https://www.example.com/hesk-script.js';

	// --> Language settings
	$hesk_settings['can_sel_lang']=0;
	$hesk_settings['language']='English';
	$hesk_settings['languages']=array(
	'English' => array('folder'=>'en','hr'=>'------ Reply above this line ------'),
	);

	// --> Database settings
	$hesk_settings['db_host']='localhost';
	$hesk_settings['db_name']='hesk';
	$hesk_settings['db_user']='test';
	$hesk_settings['db_pass']='test';
	$hesk_settings['db_pfix']='hesk_';


	// ==> HELP DESK

	// --> Help desk settings
	$hesk_settings['admin_dir']='admin';
	$hesk_settings['attach_dir']='attachments';
	$hesk_settings['cache_dir']='cache';
	$hesk_settings['max_listings']=20;
	$hesk_settings['print_font_size']=12;
	$hesk_settings['autoclose']=0;
	$hesk_settings['max_open']=0;
	$hesk_settings['due_soon']=7;
	$hesk_settings['new_top']=0;
	$hesk_settings['reply_top']=0;
    $hesk_settings['hide_replies']=0;
    $hesk_settings['limit_width']=800;

	// --> Features
	$hesk_settings['autologin']=1;
	$hesk_settings['autoassign']=1;
	$hesk_settings['require_email']=1;
	$hesk_settings['require_owner']=0;
	$hesk_settings['require_subject']=1;
	$hesk_settings['require_message']=1;
	$hesk_settings['custclose']=1;
	$hesk_settings['custopen']=1;
	$hesk_settings['rating']=1;
	$hesk_settings['cust_urgency']=1;
	$hesk_settings['sequential']=1;
	$hesk_settings['time_worked']=1;
	$hesk_settings['spam_notice']=1;
	$hesk_settings['list_users']=0;
	$hesk_settings['debug_mode']=0;
	$hesk_settings['short_link']=0;
    $hesk_settings['submitting_wait']=1;
    $hesk_settings['remember_custom_field_values']=1;
    $hesk_settings['disable_autofill_admin']=0;
    $hesk_settings['disable_autofill_customer']=0;
    $hesk_settings['extend_admin']=0;
    $hesk_settings['extend_customer']=0;
	$hesk_settings['select_cat']=0;
	$hesk_settings['select_pri']=0;
	$hesk_settings['cat_show_select']=15;
	$hesk_settings['staff_ticket_formatting']=0;
    $hesk_settings['staff_nicknames']=0;

    // --> Barcode
    $hesk_settings['barcode']=array(
        'print' => 0,
        'staff_only' => 0,
        'type' => 'C128',
        'format' => 'svg',
        'width' => 300,
        'height' => 80,
        'color' => 'black',
        'bg' => 'white',
    );

    // --> Customer Accounts
    $hesk_settings['customer_accounts']=0;
    $hesk_settings['customer_accounts_required']=0;
    $hesk_settings['customer_accounts_customer_self_register']=1;
    $hesk_settings['customer_accounts_admin_approvals']=0;
    $hesk_settings['customer_autologin']=1;
    $hesk_settings['customer_accounts_allow_email_changes']=1;
    $hesk_settings['customer_accounts_verify_email_cooldown']=15;

	// --> SPAM Prevention
	$hesk_settings['secimg_use']=1;
	$hesk_settings['secimg_sum']=$secimg_sum;
	$hesk_settings['recaptcha_use']=0;
	$hesk_settings['recaptcha_public_key']='';
	$hesk_settings['recaptcha_private_key']='';
	$hesk_settings['question_use']=0;
	$hesk_settings['question_ask']=$spam_question[0];
	$hesk_settings['question_ans']=$spam_question[1];

	// --> Security
	$hesk_settings['attempt_limit']=6;
	$hesk_settings['attempt_banmin']=60;
    $hesk_settings['flood']=3;
	$hesk_settings['reset_pass']=1;
	$hesk_settings['email_view_ticket']=1;
	$hesk_settings['x_frame_opt']=1;
    $hesk_settings['samesite']='Lax';
	$hesk_settings['force_ssl']=0;
    $hesk_settings['url_key']='';
    $hesk_settings['require_mfa']=0;
    $hesk_settings['require_mfa_customers']=0;
    $hesk_settings['elevator_duration']='60M';

	// --> Attachments
	$hesk_settings['attachments']=array (
	'use' =>  1,
	'max_number' => 2,
	'max_size' => 2097152,
	'allowed_types' => array('.gif','.jpg','.png','.zip','.rar','.csv','.doc','.docx','.xls','.xlsx','.txt','.pdf'),
	'attachment_in_email_type' => 0,
	'direct_attachment_in_email' => 0,
	'direct_attachment_in_email_no_of_files' => 2,
	'first_x_attachments' => 2,
	'file_max_size' => 512000,
	);


	// ==> KNOWLEDGEBASE

	// --> Knowledgebase settings
	$hesk_settings['kb_enable']=1;
	$hesk_settings['kb_wysiwyg']=1;
	$hesk_settings['kb_search']=2;
	$hesk_settings['kb_search_limit']=10;
	$hesk_settings['kb_views']=0;
	$hesk_settings['kb_date']=0;
	$hesk_settings['kb_recommendanswers']=1;
	$hesk_settings['kb_rating']=1;
	$hesk_settings['kb_substrart']=200;
	$hesk_settings['kb_cols']=2;
	$hesk_settings['kb_numshow']=3;
	$hesk_settings['kb_popart']=6;
	$hesk_settings['kb_latest']=6;
	$hesk_settings['kb_index_popart']=6;
	$hesk_settings['kb_index_latest']=0;
	$hesk_settings['kb_related']=5;


	// ==> EMAIL

	// --> Email sending
	$hesk_settings['noreply_mail']='noreply@example.com';
	$hesk_settings['noreply_name']='Help Desk';
	$hesk_settings['email_max_recipients']=50;
	$hesk_settings['email_formatting']=0;
	$hesk_settings['smtp']=0;
	$hesk_settings['smtp_host_name']='mail.example.com';
	$hesk_settings['smtp_host_port']=587;
	$hesk_settings['smtp_timeout']=10;
	$hesk_settings['smtp_enc']='tls';
	$hesk_settings['smtp_noval_cert']=0;
	$hesk_settings['smtp_user']='';
	$hesk_settings['smtp_password']='';
    $hesk_settings['smtp_conn_type']='basic';
    $hesk_settings['smtp_oauth_provider']=0;

	// --> Email piping
	$hesk_settings['email_piping']=0;

	// --> IMAP Fetching
	$hesk_settings['imap']=0;
	$hesk_settings['imap_job_wait']=15;
	$hesk_settings['imap_host_name']='mail.example.com';
	$hesk_settings['imap_host_port']=993;
	$hesk_settings['imap_enc']='ssl';
	$hesk_settings['imap_noval_cert']=0;
    $hesk_settings['imap_disable_GSSAPI']=0;
	$hesk_settings['imap_keep']=0;
	$hesk_settings['imap_user']='';
	$hesk_settings['imap_password']='';
    $hesk_settings['imap_conn_type']='basic';
    $hesk_settings['imap_oauth_provider']=0;
	$hesk_settings['imap_mailbox']='INBOX';

	// --> POP3 Fetching
	$hesk_settings['pop3']=0;
	$hesk_settings['pop3_job_wait']=15;
	$hesk_settings['pop3_host_name']='mail.example.com';
	$hesk_settings['pop3_host_port']=110;
	$hesk_settings['pop3_tls']=0;
	$hesk_settings['pop3_keep']=0;
	$hesk_settings['pop3_user']='';
	$hesk_settings['pop3_password']='';
    $hesk_settings['pop3_conn_type']='basic';
    $hesk_settings['pop3_oauth_provider']=0;

	$hesk_settings['strip_quoted']=1;
	$hesk_settings['eml_req_msg']=0;
	$hesk_settings['save_embedded']=1;
    $hesk_settings['email_include_to']=0;
    $hesk_settings['email_include_cc']=0;

    // --> Ignore emails
    $hesk_settings['pipe_block_noreply']=1;
    $hesk_settings['pipe_block_returned']=1;
    $hesk_settings['pipe_block_duplicate']=1;
	$hesk_settings['loop_hits']=6;
	$hesk_settings['loop_time']=300;
    $hesk_settings['pipe_customer_rejection_notification']=1;
    $hesk_settings['pipe_customer_rejection_email_cooldown_hours']=24;

	// --> Detect email typos
	$hesk_settings['detect_typos']=1;
	$hesk_settings['email_providers']=array('aim.com','aol.co.uk','aol.com','att.net','bellsouth.net','blueyonder.co.uk','bt.com','btinternet.com','btopenworld.com','charter.net','comcast.net','cox.net','earthlink.net','email.com','facebook.com','fastmail.fm','free.fr','freeserve.co.uk','gmail.com','gmx.at','gmx.ch','gmx.com','gmx.de','gmx.fr','gmx.net','gmx.us','googlemail.com','hotmail.be','hotmail.co.uk','hotmail.com','hotmail.com.ar','hotmail.com.mx','hotmail.de','hotmail.es','hotmail.fr','hushmail.com','icloud.com','inbox.com','laposte.net','lavabit.com','list.ru','live.be','live.co.uk','live.com','live.com.ar','live.com.mx','live.de','live.fr','love.com','lycos.com','mac.com','mail.com','mail.ru','me.com','msn.com','nate.com','naver.com','neuf.fr','ntlworld.com','o2.co.uk','online.de','orange.fr','orange.net','outlook.com','pobox.com','prodigy.net.mx','qq.com','rambler.ru','rocketmail.com','safe-mail.net','sbcglobal.net','t-online.de','talktalk.co.uk','tiscali.co.uk','verizon.net','virgin.net','virginmedia.com','wanadoo.co.uk','wanadoo.fr','yahoo.co.id','yahoo.co.in','yahoo.co.jp','yahoo.co.kr','yahoo.co.uk','yahoo.com','yahoo.com.ar','yahoo.com.mx','yahoo.com.ph','yahoo.com.sg','yahoo.de','yahoo.fr','yandex.com','yandex.ru','ymail.com');

	// --> Notify customer when
	$hesk_settings['notify_new']=1;
	$hesk_settings['notify_skip_spam']=1;
	$hesk_settings['notify_spam_tags']=array('Spam?}','***SPAM***','[SPAM]','SPAM-LOW:','SPAM-MED:');
	$hesk_settings['notify_closed']=1;

	// --> Other
	$hesk_settings['multi_eml']=0;
	$hesk_settings['confirm_email']=0;
	$hesk_settings['open_only']=1;

	// ==> TICKET LIST

	$hesk_settings['ticket_list']=array('trackid','lastchange','name','subject','status','lastreplier');
	$hesk_settings['customer_ticket_list']=array('id','trackid','lastchange','subject','status');

	// --> Other
	$hesk_settings['submittedformat']=2;
	$hesk_settings['updatedformat']=2;
    $hesk_settings['format_submitted']='Y-m-d H:i:s';
    $hesk_settings['format_updated']='Y-m-d H:i:s';
    $hesk_settings['email_column']=0;


	// ==> MISC

	// --> Date & Time
	$hesk_settings['timezone']=date_default_timezone_get();
    $hesk_settings['format_time']='H:i:s';
    $hesk_settings['format_date']='Y-m-d';
    $hesk_settings['format_timestamp']='Y-m-d H:i:s';
    $hesk_settings['time_display']=1;
    $hesk_settings['format_datepicker_js']='mm/dd/yyyy';
    $hesk_settings['format_datepicker_php']='m/d/Y';

	// --> Other
	$hesk_settings['ip_whois']='https://whois.domaintools.com/{IP}';
	$hesk_settings['maintenance_mode']=0;
	$hesk_settings['alink']=1;
	$hesk_settings['submit_notice']=0;
	$hesk_settings['online']=0;
	$hesk_settings['online_min']=10;
	$hesk_settings['check_updates']=1;


    // ==> LOOK & FEEL
    $hesk_settings['customer_theme']='';
    $hesk_settings['theme_overrides']=array();

	return $hesk_settings;
} // END hesk_defaultSettings()


function hesk_iDetectVersion()
{
	global $hesk_settings, $hesklang;

    // Get a list of tables from the database
    $tables = array();
    $res = hesk_dbQuery('SHOW TABLES FROM `'.hesk_dbEscape($hesk_settings['db_name']).'`');
    while ($row = hesk_dbFetchRow($res))
    {
        $tables[] = $row[0];
    }

    // If we don't have four basic tables this is not a valid HESK install
    if ( ! in_array($hesk_settings['db_pfix'].'categories', $tables) || ! in_array($hesk_settings['db_pfix'].'replies', $tables) || ! in_array($hesk_settings['db_pfix'].'tickets', $tables) || ! in_array($hesk_settings['db_pfix'].'users', $tables) )
    {
        hesk_iDatabase(3);
    }

    // Version 3.7.0 tables installed?
    if (in_array($hesk_settings['db_pfix'].'muted_emails', $tables))
    {
        // Version 3.7.8 tables installed?
        $res = hesk_dbQuery("SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password' AND table_schema = '".hesk_dbEscape($hesk_settings['db_name'])."' AND column_name = 'hash' LIMIT 0, 1");
        $row = hesk_dbFetchRow($res);
        if ($row[0] == 64)
        {
            return '3.7.8';
        }

        return '3.7.0';
    }

    // Version 3.6.0 tables installed?
    if (in_array($hesk_settings['db_pfix'].'ticket_to_collaborator', $tables))
    {
        return '3.6.0';
    }

    // Version 3.5.0 tables installed?
    if (in_array($hesk_settings['db_pfix'].'customers', $tables))
    {
        // Version 3.5.2 tables installed?
        $res = hesk_dbQuery("SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '".hesk_dbEscape($hesk_settings['db_pfix'])."tickets' AND table_schema = '".hesk_dbEscape($hesk_settings['db_name'])."' AND column_name = 'eid' LIMIT 0, 1");
        $row = hesk_dbFetchRow($res);
        if ($row[0] == 1000)
        {
            return '3.5.2';
        }

        return '3.5.0';
    }

    // Version 3.4.0 tables installed?
    if (in_array($hesk_settings['db_pfix'].'oauth_providers', $tables))
    {
        return '3.4.0';
    }

    // Version 3.3.0 tables installed?
    if (in_array($hesk_settings['db_pfix'].'auth_tokens', $tables))
    {
        return '3.3.0';
    }

    // Version 3.2.0 tables installed?
    if (in_array($hesk_settings['db_pfix'].'log_overdue', $tables))
    {
        return '3.2.0';
    }

    // Version 2.8.3 tables installed?
    $res = hesk_dbQuery("SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '".hesk_dbEscape($hesk_settings['db_pfix'])."tickets' AND table_schema = '".hesk_dbEscape($hesk_settings['db_name'])."' AND column_name = 'name' LIMIT 0, 1");
    $row = hesk_dbFetchRow($res);
    if ($row[0] == 255)
    {
        return '2.8.3';
    }

    // Version 2.8.2 tables installed?
    if (in_array($hesk_settings['db_pfix'].'service_messages', $tables))
    {
        $res = hesk_dbQuery("SHOW COLUMNS FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."service_messages` LIKE 'language'");
        if (hesk_dbNumRows($res))
        {
            return '2.8.2';
        }
    }

    // Version 2.8 tables installed?
    $res = hesk_dbQuery("SHOW COLUMNS FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` LIKE 'assignedby'");
    if (hesk_dbNumRows($res))
    {
        return '2.8';
    }

	// Version 2.7 tables installed?
	if (
		in_array($hesk_settings['db_pfix'].'custom_fields', $tables) ||
        in_array($hesk_settings['db_pfix'].'custom_statuses', $tables)
		)
	{
		return '2.7';
	}

	// Version 2.6 tables installed?
	if (
		in_array($hesk_settings['db_pfix'].'banned_emails', $tables) ||
		in_array($hesk_settings['db_pfix'].'banned_ips', $tables) ||
		in_array($hesk_settings['db_pfix'].'reply_drafts', $tables) ||
		in_array($hesk_settings['db_pfix'].'reset_password', $tables) ||
		in_array($hesk_settings['db_pfix'].'service_messages', $tables) ||
		in_array($hesk_settings['db_pfix'].'ticket_templates', $tables)
		)
	{
		return '2.6';
	}

	// Version 2.4/2.5 tables installed?
	elseif (in_array($hesk_settings['db_pfix'].'pipe_loops', $tables))
	{
		// Version 2.4 didn't have articles_private in kb_categories
		$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` WHERE `id`=1 LIMIT 1");
		$row = hesk_dbFetchAssoc($res);
		if (isset($row['articles_private']))
		{
			// This is one of the 2.5.x version
            // Database is 100% compatible, so let's be safe and return 2.5
            return '2.5';
		}
		else
		{
			return '2.4';
		}
	}

	// Version 2.3 tables installed?
	elseif (in_array($hesk_settings['db_pfix'].'online', $tables) || in_array($hesk_settings['db_pfix'].'logins', $tables))
	{
		return '2.3';
	}

	// Version 2.2 tables installed?
	elseif (in_array($hesk_settings['db_pfix'].'mail', $tables))
	{
		return '2.2';
	}

	// Version 2.1 tables installed?
	elseif (in_array($hesk_settings['db_pfix'].'kb_attachments', $tables))
	{
		return '2.1';
	}

	// Version 2.0 tables installed?
	elseif (in_array($hesk_settings['db_pfix'].'kb_articles', $tables))
	{
		return '2.0';
	}

	// Version 0.94.1 tables installed?
	elseif (in_array('hesk_attachments', $tables))
	{
		return '0.94.1';
	}

	// Version 0.94 tables installed?
	elseif (in_array('hesk_std_replies', $tables))
	{
		return '0.94';
	}

	// It's a version older than 0.94 or no tables found
	else
	{
		// Version 0.90 didn't have the notify column in users table
		$res = hesk_dbQuery("SELECT * FROM `hesk_users` WHERE `id`=1 LIMIT 1");
		$row = hesk_dbFetchAssoc($res);
		if (isset($row['notify']))
		{
			return '0.91-0.93.1';
		}
		else
		{
        	// Wow, we found someone using the very first HESK version :-)
			return '0.90';
		}
	}

} // END hesk_iDetectVersion()


function hesk_iUpdateLangauges()
{
    global $hesk_settings, $hesklang;

    $version = preg_replace('/[^a-zA-Z0-9\.]/', '', HESK_NEW_VERSION);
    $action = 'upgrade';

    foreach ($hesk_settings['languages'] as $language) {
        // Language tag, like "en" or "de"
        $tag = preg_replace('/[^a-zA-Z0-9\-]/', '', $language['folder']);

        // No need to update English language pack, it's included
        if ($tag == 'en') {
            continue;
        }

        $lang_path = HESK_PATH . 'language/';
        $dir_path = $lang_path . $tag;
        $zip_path = $dir_path . '.zip';
        $upgrade_path = $dir_path.'_old';

        try {
            // Let's do some cleanup first in case there are files/folders from previous installs
            hesk_unlink($zip_path);
            hesk_rrmdir($upgrade_path);

            // Here is where we will download the languge pack from
            $download_url = 'https://www.hesk.com/language/download.php?tag='.urlencode($tag).'&version='.urlencode($version);

            // Try using cURL
            if ( function_exists('curl_init') ) {
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_URL, $download_url);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6);
                $zip_data = curl_exec($ch);
                hesk_curl_close($ch);
            }

            // Try using a simple PHP function instead
            if (empty($zip_data)) {
                $zip_data = @file_get_contents($download_url);
            }

            // Unsuccessful download
            if (empty($zip_data)) {
                return false;
            }

            // Save the zip file and check that it exists
            file_put_contents($zip_path, $zip_data);
            if ( ! file_exists($zip_path)) {
                return false;
            }

            // We need to preserve old data for upgrades
            if ($action == 'upgrade') {
                rename($dir_path, $upgrade_path);
                if ( ! is_dir($upgrade_path)) {
                    return false;
                }
            }

            hesk_extractZip($zip_path, $lang_path, $tag);
            hesk_unlink($zip_path);

            if ( ! file_exists($dir_path . '/text.php')) {
                if ($action == 'upgrade') {
                    hesk_rrmdir($dir_path);
                    rename($upgrade_path, $dir_path);
                }
                return false;
            }

            // Copy custom-text.php
            if (file_exists($upgrade_path . '/custom-text.php')) {
                rename($upgrade_path . '/custom-text.php', $dir_path . '/custom-text.php');
            }
            // Copy plain text and html email templates in case they were modified
            $emails = array_diff(scandir($upgrade_path . '/emails/'), array('.','..','index.htm'));
            foreach ($emails as $email) {
                hesk_unlink($dir_path . '/emails/' . $email);
                rename($upgrade_path . '/emails/' . $email, $dir_path . '/emails/' . $email);
            }
            $emails = array_diff(scandir($upgrade_path . '/html_emails/'), array('.','..','index.htm'));
            foreach ($emails as $email) {
                hesk_unlink($dir_path . '/html_emails/' . $email);
                rename($upgrade_path . '/html_emails/' . $email, $dir_path . '/html_emails/' . $email);
            }
            // Remove the backup
            hesk_rrmdir($upgrade_path);
            hesk_purge_cache();
        } catch (Exception $e) {
            return false;
        }
    }

    return true;
} // END hesk_iUpdateLangauges()


function hesk_extractZip($zip_file, $destination_dir, $expected_folder) {

    if ( ! is_dir($destination_dir)) {
        @mkdir($destination_dir, 0777, true);
    }

    if ( ! is_writable($destination_dir)) {
        @chmod($destination_dir, 0777);
    }

    if (class_exists('ZipArchive')) {
        $zip = new ZipArchive;
        if ($zip->open($zip_file) === true) {
            if ( ! hesk_validateLanguageZipEntries($zip, $expected_folder)) {
                $zip->close();
                return false;
            }
            $zip->extractTo($destination_dir);
            $zip->close();
            return true;
        }
    } else {
        require(HESK_PATH . 'inc/zip/pclzip.lib.php');
        $zip = new PclZip($zip_file);
        if ( ! hesk_validateLanguageZipEntries($zip, $expected_folder)) {
            return false;
        }
        $result = $zip->extract(PCLZIP_OPT_PATH, $destination_dir);
        return true;
    }

    return false;
} // END hesk_extractZip()


function hesk_validateLanguageZipEntries($zip, $expected_folder) {

    $expected_folder = trim($expected_folder, '/\\'); // '

    if ($expected_folder === '' || preg_match('/[^a-zA-Z0-9\-]/', $expected_folder)) {
        return false;
    }

    if ($zip instanceof ZipArchive) {
        for ($i = 0; $i < $zip->numFiles; $i++) {
            if ( ! hesk_isSafeLanguageZipEntry($zip->getNameIndex($i), $expected_folder)) {
                return false;
            }
        }

        return true;
    }

    $files = $zip->listContent();

    if ( ! is_array($files)) {
        return false;
    }

    foreach ($files as $file) {
        if ( ! isset($file['filename']) || ! hesk_isSafeLanguageZipEntry($file['filename'], $expected_folder)) {
            return false;
        }
    }

    return true;
} // END hesk_validateLanguageZipEntries()


function hesk_isSafeLanguageZipEntry($filename, $expected_folder) {

    if ( ! is_string($filename) || $filename === '' || strpos($filename, "\0") !== false) {
        return false;
    }

    // Reject Windows paths, absolute paths, drive letters, and path traversal.
    if (strpos($filename, '\\') !== false || $filename[0] === '/' || preg_match('/^[a-zA-Z]:/', $filename)) { // '
        return false;
    }

    $filename = rtrim($filename, '/');

    if ($filename === '') {
        return false;
    }

    $parts = explode('/', $filename);

    foreach ($parts as $part) {
        if ($part === '' || $part === '.' || $part === '..') {
            return false;
        }
    }

    return $parts[0] === $expected_folder;
} // END hesk_isSafeLanguageZipEntry()