diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css index 077b79fbb6a5e65a4cf9136ccf686f3077b552b8..c4bd447e8ce924d267453b60b4b0faa764ce5a5f 100644 --- a/assets/default/css/theme.css +++ b/assets/default/css/theme.css @@ -568,6 +568,15 @@ label.required { position: relative; top: 6px; } +#ticketForm > table { + table-layout: fixed; +} +#ticketForm > table td { + width: 160px; +} +#ticketForm > table td + td { + width: auto; +} #ticketForm td textarea, #clientLogin td textarea, #ticketForm div textarea, diff --git a/bootstrap.php b/bootstrap.php index 7c2f1135ac00c12ee4b696ac1819049260c45509..3e01197710452ab346db3d580dbd40a5c931b3ba 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -2,7 +2,7 @@ class Bootstrap { - function init() { + static function init() { #Disable Globals if enabled....before loading config info if(ini_get('register_globals')) { ini_set('register_globals',0); @@ -218,9 +218,11 @@ class Bootstrap { define('LATIN1_UC_CHARS', 'ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖØÙÚÛÜÃ'); define('LATIN1_LC_CHARS', 'à áâãäåæçèéêëìÃîïðñòóôõöøùúûüý'); function mb_strtoupper($str) { + if (is_array($str)) $str = $str[0]; return strtoupper(strtr($str, LATIN1_LC_CHARS, LATIN1_UC_CHARS)); } function mb_strtolower($str) { + if (is_array($str)) $str = $str[0]; return strtolower(strtr($str, LATIN1_UC_CHARS, LATIN1_LC_CHARS)); } define('MB_CASE_LOWER', 1); @@ -231,16 +233,20 @@ class Bootstrap { // char is not a single-byte char switch ($mode) { case MB_CASE_LOWER: - return preg_replace_callback('/\p{Lu}+/u', - function($a) { return mb_strtolower($a); }, $str); + return preg_replace_callback('/\p{Lu}+/u', 'mb_strtolower', $str); case MB_CASE_UPPER: - return preg_replace_callback('/\p{Ll}+/u', - function($a) { return mb_strtoupper($a); }, $str); + return preg_replace_callback('/\p{Ll}+/u', 'mb_strtoupper', $str); case MB_CASE_TITLE: - return preg_replace_callback('/\b\p{Ll}/u', - function($a) { return mb_strtoupper($a); }, $str); + return preg_replace_callback('/\b\p{Ll}/u', 'mb_strtoupper', $str); } } + function mb_internal_encoding($encoding) { return 'UTF-8'; } + function mb_regex_encoding($encoding) { return 'UTF-8'; } + function mb_substr_count($haystack, $needle) { + $matches = array(); + return preg_match_all('`'.preg_quote($needle).'`u', $haystack, + $matches); + } } else { // Use UTF-8 for all multi-byte string encoding @@ -296,7 +302,6 @@ Bootstrap::init(); #CURRENT EXECUTING SCRIPT. define('THISPAGE', Misc::currentURL()); -define('THISURI', $_SERVER['REQUEST_URI']); define('DEFAULT_MAX_FILE_UPLOADS',ini_get('max_file_uploads')?ini_get('max_file_uploads'):5); define('DEFAULT_PRIORITY_ID',1); diff --git a/css/thread.css b/css/thread.css index 1312af2f38a45e73bce112edf6475ad2c4f1f642..14c0c694d7ce5e76346027181ef3404ee79506e6 100644 --- a/css/thread.css +++ b/css/thread.css @@ -396,7 +396,21 @@ line-height: 1.25rem; } -.thread-body div:not(.caption), +/* Adjust plain/text messages posted as <pre> in the thread body to show in + * a more normal font. Other <pre> elements in the ticket thread body should + * be shown with the ususal monospace font + */ +.thread-body > div > pre:first-child { + font-family: sans-serif; +} + +/* Avoid extra padding at the bottom of the thread body element */ +.thread-body :last-child, +.thread-body > div { + margin-bottom: 0 !important; +} + +.thread-body > div div:not(.caption), .thread-body p, .thread-body ul, .thread-body ol, @@ -451,10 +465,24 @@ table.thread-entry { table-layout: fixed; } +table.thread-entry th div span { + vertical-align: middle; +} +table.thread-entry th div :not(.title) { + font-weight: 600; +} +table.thread-entry th div .title { + font-weight: 400; +} +table.thread-entry th .textra { + margin-right: 1em; + display: inline-block; +} /* Inline image hovering with download link */ .image-hover { display: inline-block; position: relative; + max-width: 100%; /* Ensure image hovered is resized */ } .image-hover .caption { background-color: rgba(0,0,0,0.5); diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 058bb12a37f9c5f7c8723b695fa40d56ed1c51c2..bee2c7d6cfcd7be03684f1baedb273979b7205a5 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -211,13 +211,13 @@ class TicketsAjaxAPI extends AjaxController { 'WHERE entry.object_type="T" GROUP BY entry.object_id)'; $vals = array(); foreach (TicketForm::getInstance()->getFields() as $f) { - if ($f->get('name') && isset($req[$f->getFormName()]) + if (isset($req[$f->getFormName()]) && ($val = $req[$f->getFormName()])) { - $name = $f->get('name'); - $vals[] = "MAX(IF(field.name = '$name', ans.value_id, NULL)) as `{$name}_id`"; - $vals[] = "MAX(IF(field.name = '$name', ans.value, NULL)) as `$name`"; - $where .= " AND (dyn.`{$name}_id` = ".db_input($val) - . " OR dyn.`$name` LIKE '%".db_real_escape($val)."%')"; + $id = $f->get('id'); + $vals[] = "MAX(IF(field.id = '$id', ans.value_id, NULL)) as `f_{$id}_id`"; + $vals[] = "MAX(IF(field.id = '$id', ans.value, NULL)) as `f_$id`"; + $where .= " AND (dyn.`f_{$id}_id` = ".db_input($val) + . " OR dyn.`f_$id` LIKE '%".db_real_escape($val)."%')"; } } if ($vals) @@ -604,8 +604,9 @@ class TicketsAjaxAPI extends AjaxController { $info = array( - 'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(), $user->getName()) - ); + 'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(), + Format::htmlchars($user->getName())) + ); ob_start(); include(STAFFINC_DIR . 'templates/user.tmpl.php'); @@ -632,8 +633,9 @@ class TicketsAjaxAPI extends AjaxController { $forms = $user->getForms(); $info = array( - 'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(), $user->getName()) - ); + 'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(), + Format::htmlchars($user->getName())) + ); ob_start(); include(STAFFINC_DIR . 'templates/user.tmpl.php'); diff --git a/include/ajax.tips.php b/include/ajax.tips.php index 3b2a1d19a565fe7d812df9920857c9589c89f6b5..ed560403e945933600ad3390dea1aa508530ea8b 100644 --- a/include/ajax.tips.php +++ b/include/ajax.tips.php @@ -31,8 +31,9 @@ class HelpTipAjaxAPI extends AjaxController { // Translate links to the root path of this installation foreach ($data as $tip=>&$info) { - $info = $ost->replaceTemplateVariables($info, array( - 'config'=>$ost->getConfig())); + if ($ost) + $info = $ost->replaceTemplateVariables($info, array( + 'config'=>$ost->getConfig())); if (isset($info['links'])) foreach ($info['links'] as &$l) if ($l['href'][0] == '/') diff --git a/include/ajax.users.php b/include/ajax.users.php index e73c0b054894af1bdecf2a0f2af2d2a514ec0ce8..28b98e0190de1a2085fa5dee0c7f075634734a56 100644 --- a/include/ajax.users.php +++ b/include/ajax.users.php @@ -45,6 +45,7 @@ class UsersAjaxAPI extends AjaxController { if(($res=db_query($sql)) && db_num_rows($res)){ while(list($id,$email,$name)=db_fetch_row($res)) { + $name = Format::htmlchars($name); $users[] = array('email'=>$email, 'name'=>$name, 'info'=>"$email - $name", "id" => $id, "/bin/true" => $_REQUEST['q']); } @@ -54,9 +55,41 @@ class UsersAjaxAPI extends AjaxController { } - function getUser() { + function editUser($id) { + global $thisstaff; + + if(!$thisstaff) + Http::response(403, 'Login Required'); + elseif(!($user = User::lookup($id))) + Http::response(404, 'Unknown user'); + + $info = array( + 'title' => sprintf('Update %s', $user->getName()) + ); + $forms = $user->getForms(); + + include(STAFFINC_DIR . 'templates/user.tmpl.php'); + } + + function updateUser($id) { + global $thisstaff; + + if(!$thisstaff) + Http::response(403, 'Login Required'); + elseif(!($user = User::lookup($id))) + Http::response(404, 'Unknown user'); + + $errors = array(); + if($user->updateInfo($_POST, $errors)) + Http::response(201, $user->to_json()); + + $forms = $user->getForms(); + include(STAFFINC_DIR . 'templates/user.tmpl.php'); + } + + function getUser($id=false) { - if(($user=User::lookup($_REQUEST['id']))) + if(($user=User::lookup(($id) ? $id : $_REQUEST['id']))) Http::response(201, $user->to_json()); $info = array('error' =>'Unknown or invalid user'); diff --git a/include/class.config.php b/include/class.config.php index e6c9351f9bc23040db0704e5d84bc7661a1befc2..06607fdc52393e084f8fdc9dde376b4c8108a1e1 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -858,7 +858,7 @@ class OsticketConfig extends Config { 'autolock_minutes'=>$vars['autolock_minutes'], 'use_email_priority'=>isset($vars['use_email_priority'])?1:0, 'enable_captcha'=>isset($vars['enable_captcha'])?1:0, - 'auto_assign_reopened_tickets'=>isset($vars['auto_assign_reopened_tickets'])?1:0, + 'auto_claim_tickets'=>isset($vars['auto_claim_tickets'])?1:0, 'show_assigned_tickets'=>isset($vars['show_assigned_tickets'])?1:0, 'show_answered_tickets'=>isset($vars['show_answered_tickets'])?1:0, 'show_related_tickets'=>isset($vars['show_related_tickets'])?1:0, diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 081ceabc7d342a110791065e8f48c13296c3b045..52e3109356db760e6a9d412c7782b5697b9891ec 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -39,10 +39,11 @@ class DynamicForm extends VerySimpleModel { var $_form; var $_fields; + var $_has_data = false; var $_dfields; - function getFields() { - if (!isset($this->_fields)) { + function getFields($cache=true) { + if (!isset($this->_fields) || !$cache) { $this->_fields = array(); foreach ($this->getDynamicFields() as $f) // TODO: Index by field name or id @@ -83,7 +84,7 @@ class DynamicForm extends VerySimpleModel { function getForm($source=false) { if (!$this->_form || $source) { - $fields = $this->getFields(); + $fields = $this->getFields($this->_has_data); $this->_form = new Form($fields, $source, array( 'title'=>$this->title, 'instructions'=>$this->instructions)); } @@ -102,6 +103,7 @@ class DynamicForm extends VerySimpleModel { function data($data) { if ($data instanceof DynamicFormEntry) { $this->_fields = $data->getFields(); + $this->_has_data = true; } } @@ -144,8 +146,8 @@ class UserForm extends DynamicForm { return $os->filter(array('type'=>'U')); } - function getFields() { - $fields = parent::getFields(); + function getFields($cache=true) { + $fields = parent::getFields($cache); foreach ($fields as $f) { if ($f->get('name') == 'email') { $f->getConfiguration(); @@ -535,7 +537,7 @@ class DynamicFormEntry extends VerySimpleModel { if ($field->hasData() && !$field->isPresentationOnly()) $a->save(); } - $this->_values = array(); + $this->_values = null; } function delete() { @@ -619,6 +621,10 @@ class DynamicFormEntryAnswer extends VerySimpleModel { return $this->getField()->toString($this->getValue()); } + function display() { + return $this->getField()->display($this->getValue()); + } + function asVar() { return $this->toString(); } diff --git a/include/class.forms.php b/include/class.forms.php index 67b05e16ddf8aaae0f6e37056b2cade65b5595db..cfa0a1cef0deb5528dbd3a03f5a271c91a7db335 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -290,6 +290,13 @@ class FormField { return $value; } + /** + * Returns an HTML friendly value for the data in the field. + */ + function display($value) { + return Format::htmlchars($this->toString($value)); + } + function getLabel() { return $this->get('label'); } /** @@ -532,6 +539,14 @@ class TextareaField extends FormField { 'configuration'=>array('desc'=>'Allow HTML input in this box'))), ); } + + function display($value) { + $config = $this->getConfiguration(); + if ($config['html']) + return Format::safe_html($value); + else + return Format::htmlchars($value); + } } class PhoneField extends FormField { diff --git a/include/class.osticket.php b/include/class.osticket.php index 0e76fe3e6f4f02c6c79b72081182e90d2c002c91..e1c7a9075b656bef894a2cdf774f3947ca9754b3 100644 --- a/include/class.osticket.php +++ b/include/class.osticket.php @@ -366,8 +366,7 @@ class osTicket { return null; } - /* static */ - function get_root_path($dir) { + static function get_root_path($dir) { /* If run from the commandline, DOCUMENT_ROOT will not be set. It is * also likely that the ROOT_PATH will not be necessary, so don't @@ -398,7 +397,8 @@ class osTicket { * the osTicket installation. That is removed from SCRIPT_NAME. * What's left is the ROOT_PATH. */ - $frame = array_pop(debug_backtrace(false)); + $bt = debug_backtrace(false); + $frame = array_pop($bt); $file = str_replace('\\','/', $frame['file']); $path = substr($file, strlen(ROOT_DIR)); if($path && ($pos=strpos($_SERVER['SCRIPT_NAME'], $path))!==false) diff --git a/include/class.setup.php b/include/class.setup.php index f281da61b59a910063af511bcb2a0d0ff9da7cf7..cc13fe2f5e60b273f4aff26dbcb754aebfd917c0 100644 --- a/include/class.setup.php +++ b/include/class.setup.php @@ -93,6 +93,10 @@ Class SetupWizard { return (extension_loaded('mysqli')); } + function check_mysql_version() { + return (version_compare(db_version(), $this->getMySQLVersion())>=0); + } + function check_prereq() { return ($this->check_php() && $this->check_mysql()); } diff --git a/include/class.ticket.php b/include/class.ticket.php index 318d205358e4775aeeb5e42b377f689dde6af4cb..273d8e292773aeaf0b9720eb239001db010be9a8 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -23,7 +23,6 @@ include_once(INCLUDE_DIR.'class.topic.php'); include_once(INCLUDE_DIR.'class.lock.php'); include_once(INCLUDE_DIR.'class.file.php'); include_once(INCLUDE_DIR.'class.attachment.php'); -include_once(INCLUDE_DIR.'class.pdf.php'); include_once(INCLUDE_DIR.'class.banlist.php'); include_once(INCLUDE_DIR.'class.template.php'); include_once(INCLUDE_DIR.'class.variable.php'); @@ -304,16 +303,6 @@ class Ticket { return ''; } - function getPhone() { - list($phone, $ext) = $this->getPhoneNumber(); - return $phone; - } - - function getPhoneExt() { - list($phone, $ext) = $this->getPhoneNumber(); - return $ext; - } - function getPhoneNumber() { return (string)$this->getOwner()->getPhoneNumber(); } @@ -1150,6 +1139,7 @@ class Ticket { return call_user_func(array($this, 'get'.ucfirst($tag))); switch(strtolower($tag)) { + case 'phone': case 'phone_number': return $this->getPhoneNumber(); break; @@ -1761,6 +1751,7 @@ class Ticket { //Print ticket... export the ticket thread as PDF. function pdfExport($psize='Letter', $notes=false) { + require_once(INCLUDE_DIR.'class.pdf.php'); $pdf = new Ticket2PDF($this, $psize, $notes); $name='Ticket-'.$this->getExtId().'.pdf'; $pdf->Output($name, 'I'); @@ -1931,47 +1922,57 @@ class Ticket { if(!$staff || (!is_object($staff) && !($staff=Staff::lookup($staff))) || !$staff->isStaff()) return null; - $sql='SELECT count(open.ticket_id) as open, count(answered.ticket_id) as answered ' - .' ,count(overdue.ticket_id) as overdue, count(assigned.ticket_id) as assigned, count(closed.ticket_id) as closed ' - .' FROM '.TICKET_TABLE.' ticket ' - .' LEFT JOIN '.TICKET_TABLE.' open - ON (open.ticket_id=ticket.ticket_id - AND open.status=\'open\' - AND open.isanswered=0 - '.((!($cfg->showAssignedTickets() || $staff->showAssignedTickets()))? - ' AND open.staff_id=0 ':'').') ' - .' LEFT JOIN '.TICKET_TABLE.' answered - ON (answered.ticket_id=ticket.ticket_id - AND answered.status=\'open\' - AND answered.isanswered=1) ' - .' LEFT JOIN '.TICKET_TABLE.' overdue - ON (overdue.ticket_id=ticket.ticket_id - AND overdue.status=\'open\' - AND overdue.isoverdue=1) ' - .' LEFT JOIN '.TICKET_TABLE.' assigned - ON (assigned.ticket_id=ticket.ticket_id - AND assigned.status=\'open\' - AND assigned.staff_id='.db_input($staff->getId()).')' - .' LEFT JOIN '.TICKET_TABLE.' closed - ON (closed.ticket_id=ticket.ticket_id - AND closed.status=\'closed\' )' - .' WHERE (ticket.staff_id='.db_input($staff->getId()); + $where = array(); + $where2 = ''; if(($teams=$staff->getTeams())) - $sql.=' OR ticket.team_id IN('.implode(',', db_input(array_filter($teams))).')'; + $where[] = 'ticket.team_id IN('.implode(',', db_input(array_filter($teams))).')'; if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets. - $sql.=' OR ticket.dept_id IN('.implode(',', db_input($depts)).') '; - - $sql.=')'; + $where[] = 'ticket.dept_id IN('.implode(',', db_input($depts)).') '; if(!$cfg || !($cfg->showAssignedTickets() || $staff->showAssignedTickets())) - $sql.=' AND (ticket.staff_id=0 OR ticket.staff_id='.db_input($staff->getId()).') '; - - return db_fetch_array(db_query($sql)); + $where2 =' AND (ticket.staff_id=0 OR ticket.staff_id='.db_input($staff->getId()).') '; + $where = implode(' OR ', $where); + if ($where) $where = 'AND ( '.$where.' ) '; + + $sql = 'SELECT \'open\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'WHERE ticket.status = \'open\' ' + .'AND ticket.isanswered =0 ' + . $where . $where2 + + .'UNION SELECT \'answered\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'WHERE ticket.status = \'open\' ' + .'AND ticket.isanswered =1 ' + . $where + + .'UNION SELECT \'overdue\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'WHERE ticket.status = \'open\' ' + .'AND ticket.isoverdue =1 ' + . $where + + .'UNION SELECT \'assigned\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'WHERE ticket.status = \'open\' ' + .'AND ticket.staff_id = ' . db_input($staff->getId()) . ' ' + . $where + + .'UNION SELECT \'closed\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'WHERE ticket.status = \'closed\' ' + . $where; + + $res = db_query($sql); + $stats = array(); + while($row = db_fetch_row($res)) { + $stats[$row[0]] = $row[1]; + } + return $stats; } - /* Quick client's tickets stats @email - valid email. */ diff --git a/include/class.upgrader.php b/include/class.upgrader.php index 69af4dfde6ee3f1b96b583ac3d00abcf07ff8562..c08ff6c43cbbd4e9e347efc27108873d1695e0c5 100644 --- a/include/class.upgrader.php +++ b/include/class.upgrader.php @@ -103,20 +103,16 @@ class Upgrader { return $this->getCurrentStream()->upgrade(); } - function check_prereq() { - if ($this->getCurrentStream()) - return $this->getCurrentStream()->check_prereq(); - } - function check_php() { - if ($this->getCurrentStream()) - return $this->getCurrentStream()->check_php(); - } - function check_mysql() { - if ($this->getCurrentStream()) - return $this->getCurrentStream()->check_mysql(); + function __call($what, $args) { + if ($this->getCurrentStream()) { + $callable = array($this->getCurrentStream(), $what); + if (!is_callable($callable)) + throw new Exception('InternalError: Upgrader method not callable: ' + . $what); + return call_user_func_array($callable, $args); + } } - function getTask() { if($this->getCurrentStream()) return $this->getCurrentStream()->getTask(); @@ -213,6 +209,9 @@ class StreamUpgrader extends SetupWizard { $this->migrater = null; } + function check_prereq() { + return (parent::check_prereq() && $this->check_mysql_version()); + } function onError($error) { global $ost, $thisstaff; diff --git a/include/class.user.php b/include/class.user.php index 85bffe026d3769715c466219d487749de92f6fca..3281d38c2865ee013e62f563c11c116fd6b9f490 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -143,7 +143,8 @@ class User extends UserModel { $info = array( 'id' => $this->getId(), 'name' => (string) $this->getName(), - 'email' => (string) $this->getEmail()); + 'email' => (string) $this->getEmail(), + 'phone' => (string) $this->getPhoneNumber()); return JsonDataEncoder::encode($info); } diff --git a/include/client/header.inc.php b/include/client/header.inc.php index e03f36cbb1aa29b762fc8f4e5eaadba7a51aa489..7266060fbf2f50160e54b1a275f3b4e90fafc87e 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -43,7 +43,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <p> <?php if($thisclient && is_object($thisclient) && $thisclient->isValid()) { - echo $thisclient->getName().' - '; + echo Format::htmlchars($thisclient->getName()).' - '; ?> <?php if($cfg->showRelatedTickets()) {?> diff --git a/include/client/view.inc.php b/include/client/view.inc.php index f7884302e3db5256af9a92272cf1696369106297..910d021de0fd98940996d90b311fb54a848a134e 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -39,7 +39,7 @@ if(!$dept || !$dept->isPublic()) <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0"> <tr> <th width="100">Name:</th> - <td><?php echo ucfirst($ticket->getName()); ?></td> + <td><?php echo ucfirst(Format::htmlchars($ticket->getName())); ?></td> </tr> <tr> <th width="100">Email:</th> @@ -70,7 +70,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) { <tr> <th width="100"><?php echo $answer->getField()->get('label'); ?>:</th> - <td><?php echo $answer->toString(); ?></td> + <td><?php echo $answer->display(); ?></td> </tr> <?php } ?> </table></td> diff --git a/include/staff/cannedresponse.inc.php b/include/staff/cannedresponse.inc.php index 4e38ff9649cb3447e3d4a6d98b6277ea144c32d4..d82baad74ffb9a98cd6dc494f3ab4ce9ae55c8fb 100644 --- a/include/staff/cannedresponse.inc.php +++ b/include/staff/cannedresponse.inc.php @@ -28,8 +28,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2>Canned Response</h2> - <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> + <tr><td></td><td></td></tr> <!-- For fixed table layout --> <tr> <th colspan="2"> <h4><?php echo $title; ?></h4> diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php index de9ae77e8eda8790e0f018d58442b6f25b38fb35..a5019a722707d7941a731033eccd48be1abde9fd 100644 --- a/include/staff/dynamic-form.inc.php +++ b/include/staff/dynamic-form.inc.php @@ -37,6 +37,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <td><input type="text" name="title" size="40" value="<?php echo $info['title']; ?>"/> <i class="help-tip icon-question-sign" href="#form_title"></i> + <font class="error"><?php + if ($errors['title']) echo '<br/>'; echo $errors['title']; ?></font> </td> </tr> <tr> diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php index 476cf5b0af0e9a4a9cb63d4ec8edc6051c455a27..8b390c3de8d43762ce79f5560d07486e2c8bc636 100644 --- a/include/staff/faq.inc.php +++ b/include/staff/faq.inc.php @@ -30,8 +30,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2>FAQ</h2> - <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> + <tr><td></td><td></td></tr> <!-- For fixed table layout --> <tr> <th colspan="2"> <h4><?php echo $title; ?></h4> @@ -125,7 +126,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <em><strong>Help Topics</strong>: Check all help topics related to this FAQ.</em> </th> </tr> - <tr><td> + <tr><td colspan="2"> <?php while(list($topicId,$topic)=db_fetch_row($res)) { echo sprintf('<input type="checkbox" name="topics[]" value="%d" %s>%s<br>', diff --git a/include/staff/page.inc.php b/include/staff/page.inc.php index b76a40607b58614c031829b62431e66c9496183e..884d7377eacb64d6152eda71373e504716a290fd 100644 --- a/include/staff/page.inc.php +++ b/include/staff/page.inc.php @@ -32,8 +32,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2>Site Pages</h2> - <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> + <tr><td></td><td></td></tr> <!-- For fixed table layout --> <tr> <th colspan="2"> <h4><?php echo $title; ?></h4> diff --git a/include/staff/settings-pages.inc.php b/include/staff/settings-pages.inc.php index e0f834107810ade075abaf1c9c3f9afeb609a52b..7b75501d55fe8d1d9706eda5bcfe91bab72ded64 100644 --- a/include/staff/settings-pages.inc.php +++ b/include/staff/settings-pages.inc.php @@ -15,7 +15,9 @@ $pages = Page::getPages(); </tr></thead> <tbody> <?php - $ost->company->getForm()->render(); + $form = $ost->company->getForm(); + $form->addMissingFields(); + $form->render(); ?> </tbody> <thead> diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php index 8a6edd8f8771ca1cd583f3d1801eb1f72925969d..fcc8ec2ca3624364bc1b668aebdf843f00036fbd 100644 --- a/include/staff/templates/user-lookup.tmpl.php +++ b/include/staff/templates/user-lookup.tmpl.php @@ -1,3 +1,4 @@ +<div id="the-lookup-form"> <h3><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> @@ -15,8 +16,23 @@ if ($info['error']) { <i class="icon-user icon-4x pull-left icon-border"></i> <a class="action-button pull-right" style="overflow:inherit" id="unselect-user" href="#"><i class="icon-remove"></i> Add New User</a> - <div><strong id="user-name"><?php echo $user ? $user->getName() : ''; ?></strong></div> + <div><strong id="user-name"><?php echo $user ? Format::htmlchars($user->getName()) : ''; ?></strong></div> <div><<span id="user-email"><?php echo $user ? $user->getEmail() : ''; ?></span>></div> +<?php if ($user) { ?> + <table style="margin-top: 1em;"> +<?php foreach ($user->getDynamicData() as $entry) { ?> + <tr><td colspan="2" style="border-bottom: 1px dotted black"><strong><?php + echo $entry->getForm()->get('title'); ?></strong></td></tr> +<?php foreach ($entry->getAnswers() as $a) { ?> + <tr style="vertical-align:top"><td style="width:30%;border-bottom: 1px dotted #ccc"><?php echo Format::htmlchars($a->getField()->get('label')); + ?>:</td> + <td style="border-bottom: 1px dotted #ccc"><?php echo $a->display(); ?></td> + </tr> +<?php } +} +?> +</table> +<?php } ?> <div class="clear"></div> <hr> <p class="full-width"> @@ -49,6 +65,7 @@ if ($info['error']) { </form> </div> <div class="clear"></div> +</div> <script type="text/javascript"> $(function() { $('#user-search').typeahead({ @@ -62,12 +79,9 @@ $(function() { }); }, onselect: function (obj) { - $('#user-name').text(obj.name); - $('#user-email').text(obj.email); - $('#user-id').val(obj.id); - $('div#selected-user-info').show(); - $('div#new-user-form').hide(); - $('#user-search').val(''); + $('#the-lookup-form').load( + "ajax.php/users/select/"+obj.id + ); }, property: "/bin/true" }); diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php index 37528cc52d13deff96f20c7062231503c43ec45a..456176c8139c37f4a3e040de20753eafa5945a36 100644 --- a/include/staff/templates/user.tmpl.php +++ b/include/staff/templates/user.tmpl.php @@ -1,6 +1,6 @@ <?php if (!$info['title']) - $info['title'] = $user->getName(); + $info['title'] = Format::htmlchars($user->getName()); ?> <h3><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> @@ -20,9 +20,22 @@ if ($info['error']) { <?php } ?> <div><b><a href="#" id="edituser"><i class="icon-edit"></i> <?php - echo $user->getName(); ?></a></b></div> + echo Format::htmlchars($user->getName()); ?></a></b></div> <div><<?php echo $user->getEmail(); ?>></div> - <div><?php echo $user->getPhoneNumber(); ?></div> + <table style="margin-top: 1em;"> +<?php foreach ($user->getDynamicData() as $entry) { +?> + <tr><td colspan="2" style="border-bottom: 1px dotted black"><strong><?php + echo $entry->getForm()->get('title'); ?></strong></td></tr> +<?php foreach ($entry->getAnswers() as $a) { ?> + <tr style="vertical-align:top"><td style="width:30%;border-bottom: 1px dotted #ccc"><?php echo Format::htmlchars($a->getField()->get('label')); + ?>:</td> + <td style="border-bottom: 1px dotted #ccc"><?php echo $a->display(); ?></td> + </tr> +<?php } +} +?> + </table> <div class="clear"></div> <hr> <div class="faded">Last updated <b><?php echo Format::db_datetime($user->getUpdateDate()); ?> </b></div> @@ -47,7 +60,8 @@ if ($ticket && $ticket->getOwnerId() == $user->getId()) <p class="full-width"> <span class="buttons" style="float:left"> <input type="reset" value="Reset"> - <input type="button" name="cancel" class="<?php echo $user ? 'cancel' : 'close' ?>" value="Cancel"> + <input type="button" name="cancel" class="<?php + echo ($ticket && $user) ? 'cancel' : 'close' ?>" value="Cancel"> </span> <span class="buttons" style="float:right"> <input type="submit" value="Update User"> diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php index 6294cda9205fda70173468c4fad871c8e8c3ce38..cddb2ee3c6b36f248110fb85833cdb87760b9b9a 100644 --- a/include/staff/ticket-edit.inc.php +++ b/include/staff/ticket-edit.inc.php @@ -25,8 +25,17 @@ if ($_POST) ?> <tr><td>Client:</td><td> <div id="client-info"> - <span id="client-name"><?php echo $user->getName(); ?></span> - <span id="client-email"><<?php echo $user->getEmail(); ?>></span> + <a href="#" onclick="javascript: + $.userLookup('ajax.php/users/<?php echo $ticket->getOwnerId(); ?>/edit', + function (user) { + $('#client-name').text(user.name); + $('#client-email').text(user.email); + }); + return false; + "><i class="icon-user"></i> + <span id="client-name"><?php echo Format::htmlchars($user->getName()); ?></span> + <<span id="client-email"><?php echo $user->getEmail(); ?></span>> + </a> <a class="action-button" style="float:none;overflow:inherit" href="#" onclick="javascript: $.userLookup('ajax.php/tickets/<?php echo $ticket->getId(); ?>/change-user', diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index a143ebb80fe42cf40fb984473e7fc42bfccc2cec..1c49b407308ba09a9487c4a465e9986a13db9c2f 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -8,8 +8,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="hidden" name="do" value="create"> <input type="hidden" name="a" value="open"> <h2>Open New Ticket</h2> - <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> + <!-- This looks empty - but beware, with fixed table layout, the user + agent will usually only consult the cells in the first row to + construct the column widths of the entire toable. Therefore, the + first row needs to have two cells --> + <tr><td></td><td></td></tr> <tr> <th colspan="2"> <h4>New Ticket</h4> @@ -27,8 +32,17 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <tr><td>Client:</td><td> <div id="client-info"> <input type="hidden" name="uid" id="uid" value="<?php echo $user->getId(); ?>" /> + <a href="#" onclick="javascript: + $.userLookup('ajax.php/users/<?php echo $user->getId(); ?>/edit', + function (user) { + $('#client-name').text(user.name); + $('#client-email').text(user.email); + }); + return false; + "><i class="icon-user"></i> <span id="client-name"><?php echo $user->getName(); ?></span> - <span id="client-email"><<?php echo $user->getEmail(); ?>></span> + <<span id="client-email"><?php echo $user->getEmail(); ?></span>> + </a> <a class="action-button" style="float:none;overflow:inherit" href="#" onclick="javascript: $.userLookup('ajax.php/users/select/'+$('input#uid').val(), diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 275bd2c2be091714938351194cdf95b7adc332b6..bc6a25e3fd57d8d859f8dd6de2af2113e6235801 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -153,6 +153,7 @@ if($ticket->isOverdue()) function (user) { $('#user-'+user.id+'-name').text(user.name); $('#user-'+user.id+'-email').text(user.email); + $('#user-'+user.id+'-phone').text(user.phone); $('#user-to-name').text(user.name); $('#user-to-email').text(user.email); }); @@ -191,7 +192,9 @@ if($ticket->isOverdue()) </tr> <tr> <th>Phone:</th> - <td><?php echo $ticket->getPhoneNumber(); ?></td> + <td> + <span id="user-<?php echo $ticket->getOwnerId(); ?>-phone"><?php echo $ticket->getPhoneNumber(); ?></span> + </td> </tr> <tr> <th>Source:</th> @@ -299,13 +302,13 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) { <td colspan="2"> <table cellspacing="0" cellpadding="4" width="100%" border="0"> <?php foreach($answers as $a) { - if (!$a->toString()) continue; ?> + if (!($v = $a->display())) continue; ?> <tr> <th width="100"><?php echo $a->getField()->get('label'); ?>:</th> <td><?php - echo $a->toString(); + echo $v; ?></td> </tr> <?php } ?> @@ -337,10 +340,20 @@ $tcount+= $ticket->getNumNotes(); ?> <table class="thread-entry <?php echo $threadTypes[$entry['thread_type']]; ?>" cellspacing="0" cellpadding="1" width="940" border="0"> <tr> - <th width="auto"><?php echo Format::db_datetime($entry['created']);?></th> - <th width="440"><span><?php echo $entry['title']; ?></span></th> - <th width="auto" class="textra" style="text-align:right"></th> - <th width="auto" class="tmeta"><?php echo Format::htmlchars($entry['poster']); ?></th> + <th colspan="4" width="100%"> + <div> + <span style="display:inline-block"><?php + echo Format::db_datetime($entry['created']);?></span> + <span style="display:inline-block;padding-left:1em" class="faded title"><?php + echo Format::truncate($entry['title'], 100); ?></span> + <span style="float:right;white-space:no-wrap;display:inline-block"> + <span style="vertical-align:middle;" class="textra"></span> + <span style="vertical-align:middle;" + class="tmeta faded title"><?php + echo Format::htmlchars($entry['poster']); ?></span> + </span> + </div> + </th> </tr> <tr><td colspan="4" class="thread-body" id="thread-id-<?php echo $entry['id']; ?>"><div><?php @@ -412,6 +425,7 @@ $tcount+= $ticket->getNumNotes(); </td> <td> <?php + # XXX: Add user-to-name and user-to-email HTML ID#s $to =sprintf('%s <%s>', $ticket->getName(), $ticket->getReplyToEmail()); $emailReply = (!isset($info['emailreply']) || $info['emailreply']); ?> @@ -918,8 +932,10 @@ $tcount+= $ticket->getNumNotes(); Are you sure want to <b>unassign</b> ticket from <b><?php echo $ticket->getAssigned(); ?></b>? </p> <p class="confirm-action" style="display:none;" id="changeuser-confirm"> + <p id="msg_warning"> + <b><?php echo Format::htmlchars($ticket->getName()); ?></b> <<?php echo $ticket->getEmail(); ?>> will no longer have access to the ticket. + </p> Are you sure want to <b>change</b> ticket owner to <b><span id="newuser">this guy</span></b>? - <br><br><b><?php echo $ticket->getName(); ?></b> <<?php echo $ticket->getEmail(); ?>> will no longer have access to the ticket. </p> <p class="confirm-action" style="display:none;" id="delete-confirm"> <font color="red"><strong>Are you sure you want to DELETE this ticket?</strong></font> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 16fc4e1e2ff2323f2fb7021707fb95f3b7b40a6d..e463e515d88a0afa7c7e013f57adf9f411d0dc9a 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -367,7 +367,7 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. $lc=Format::truncate($row['dept_name'],40); } $tid=$row['ticketID']; - $subject = Format::truncate($row['subject'],40); + $subject = Format::htmlchars(Format::truncate($row['subject'],40)); $threadcount=$row['thread_count']; if(!strcasecmp($row['status'],'open') && !$row['isanswered'] && !$row['lock_id']) { $tid=sprintf('<b>%s</b>',$tid); diff --git a/include/upgrader/prereq.inc.php b/include/upgrader/prereq.inc.php index ac2b0b2febf2d454604d5f03476ea9b83c6bbb11..98665cbc9dece47058ff2526164cb2cf4634fa3b 100644 --- a/include/upgrader/prereq.inc.php +++ b/include/upgrader/prereq.inc.php @@ -17,7 +17,9 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access D <li class="<?php echo $upgrader->check_php()?'yes':'no'; ?>"> PHP v5.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li> <li class="<?php echo $upgrader->check_mysql()?'yes':'no'; ?>"> - MySQL v5.0 or greater - (<small><b><?php echo extension_loaded('mysql')?'module loaded':'missing!'; ?></b></small>)</li> + MySQLi extension for PHP - (<small><b><?php echo extension_loaded('mysqli')?'module loaded':'missing!'; ?></b></small>)</li> + <li class="<?php echo $upgrader->check_mysql_version()?'yes':'no'; ?>"> + MySQL v5.0 or greater - (<small><b><?php echo db_version(); ?></b></small>)</li> </ul> <h3>Highly Recommended:</h3> We highly recommend that you follow the steps below. diff --git a/main.inc.php b/main.inc.php index 924f728886779c0cd668d07376cee0f745596a6b..7feb271e7743274b02b2227ac29e68f4e67f3f7e 100644 --- a/main.inc.php +++ b/main.inc.php @@ -23,8 +23,8 @@ if(isset($_SERVER['SCRIPT_NAME']) require('bootstrap.php'); Bootstrap::loadConfig(); Bootstrap::defineTables(TABLE_PREFIX); -Bootstrap::loadCode(); Bootstrap::i18n_prep(); +Bootstrap::loadCode(); Bootstrap::connect(); if(!($ost=osTicket::start()) || !($cfg = $ost->getConfig())) diff --git a/scp/ajax.php b/scp/ajax.php index a8a407acceb30705a62bc31bc74fd48f16ac3a42..397d1f44b563cb2d4e8221d7d8dd5f71229330b6 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -60,6 +60,8 @@ $dispatcher = patterns('', url('^/users', patterns('ajax.users.php:UsersAjaxAPI', url_get('^$', 'search'), url_get('^/(?P<id>\d+)$', 'getUser'), + url_post('^/(?P<id>\d+)$', 'updateUser'), + url_get('^/(?P<id>\d+)/edit$', 'editUser'), url_get('^/lookup$', 'getUser'), url_get('^/lookup/form$', 'getLookupForm'), url_post('^/lookup/form$', 'addUser'), diff --git a/scp/css/scp.css b/scp/css/scp.css index 1ed00d4dbb4fe6bf044bc506268ef787e4cac89f..d3eaceb6637dec2e8e7071a371c6c74635cee883 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -546,6 +546,17 @@ a.print { border-bottom:1px solid #ddd; } +.form_table.fixed { + table-layout: fixed; + border-collapse: collapse; + width: 100%; +} +.form_table.fixed td { + width: 180px; +} +.form_table.fixed td + td { + width: auto; +} td.multi-line { vertical-align:top; @@ -792,21 +803,6 @@ h2 .reload { text-align:right; } -#ticket_thread table th.tmeta { - font-weight:bold; - font-size:10pt; - color:#888; - text-align:right; - padding-right:15px; -} - -#ticket_thread table th span { - font-weight:normal; - font-size:10pt; - color:#888; - padding-left:5px; -} - #ticket_thread > .message th { background:#C3D9FF; } @@ -902,8 +898,8 @@ h2 .reload { border-top:none; } -#response_options > table { - width:928px; +#response_options > form > table { + table-layout: fixed; } #response_options > table td { @@ -1023,6 +1019,7 @@ h2 .reload { position:absolute; top:0.3em; right:0.5em; + text-decoration:none; } .tip_shadow { @@ -1057,7 +1054,6 @@ h2 .reload { .tip_menu li a { display:block; width:auto; - _width:0; float:left; padding:0 10px; border-right:1px solid #ddd; diff --git a/scp/forms.php b/scp/forms.php index ef697162bc848b23383310185dbc80eb1066e59b..5af2dd327781d18adca740b46ad654eb272929db 100644 --- a/scp/forms.php +++ b/scp/forms.php @@ -8,12 +8,15 @@ if($_REQUEST['id'] && !($form=DynamicForm::lookup($_REQUEST['id']))) if($_POST) { $fields = array('title', 'notes', 'instructions'); - $required = array('subject'); + $required = array('title'); $max_sort = 0; switch(strtolower($_POST['do'])) { case 'update': foreach ($fields as $f) - if (isset($_POST[$f])) + if (in_array($f, $required) && !$_POST[$f]) + $errors[$f] = sprintf('%s is required', + mb_convert_case($f, MB_CASE_TITLE)); + elseif (isset($_POST[$f])) $form->set($f, $_POST[$f]); $form->save(true); $names = array(); @@ -107,6 +110,8 @@ if($_POST) { } if ($errors) $errors['err'] = 'Unable to commit form. Check validation errors'; + else + $msg = 'Custom form successfully updated'; } $page='dynamic-forms.inc.php'; diff --git a/scp/staff.inc.php b/scp/staff.inc.php index 0c835d46a780a5acf439af2268da039a73335905..359663a4c6fccd04dc466126c759d71a89acb5a4 100644 --- a/scp/staff.inc.php +++ b/scp/staff.inc.php @@ -49,7 +49,8 @@ require_once(INCLUDE_DIR.'class.csrf.php'); if(!function_exists('staffLoginPage')) { //Ajax interface can pre-declare the function to trap expired sessions. function staffLoginPage($msg) { global $ost, $cfg; - $_SESSION['_staff']['auth']['dest']=THISURI; + $_SESSION['_staff']['auth']['dest'] = + '/' . ltrim($_SERVER['REQUEST_URI'], '/'); $_SESSION['_staff']['auth']['msg']=$msg; require(SCP_DIR.'login.php'); exit; diff --git a/scp/upgrade.php b/scp/upgrade.php index 072ca79781e9fb1f9737a7350851fe37ad944941..5664c63f5167c231da937f962a14f759ec374deb 100644 --- a/scp/upgrade.php +++ b/scp/upgrade.php @@ -35,7 +35,7 @@ if($_POST && $_POST['s'] && !$upgrader->isAborted()) { } break; case 'upgrade': //Manual upgrade.... when JS (ajax) is not supported. - if($upgrader->getPendingTask()) { + if($upgrader->getTask()) { $upgrader->doTask(); } elseif($ost->isUpgradePending() && $upgrader->isUpgradable()) { $upgrader->upgrade(); diff --git a/setup/css/wizard.css b/setup/css/wizard.css index c92b05447a6acb9992ea21be3ce13581840784a9..2dd02eced36596ac0fb570c21a6205e58de00023 100644 --- a/setup/css/wizard.css +++ b/setup/css/wizard.css @@ -1,3 +1,4 @@ +@import url("../../css/font-awesome.min.css"); body { background: url('../images/background.jpg?1312906017') top left repeat-x white; font-family: arial, helvetica, sans-serif; font-size: 10pt; color: #000; margin: 0; padding: 0; } #wizard { background: #fff; width: 800px; margin: 30px auto; padding: 10px; border: 1px solid #2a67ac; border-right: 2px solid #2a67ac; border-bottom: 3px solid #2a67ac; overflow: hidden; margin-bottom:5px;} @@ -60,13 +61,117 @@ form .row input { background: #fff; height: 22px; padding: 0; line-height: 22px; form .row label, form .row span { display: block; float: left; width: 150px; line-height: 24px; } form .row span { width: 600px; color: #666666; } -.tip_box { display: block; height: 30px; position: absolute; z-index: 1000; } - -.tip_arrow { display: block; position: absolute; top: 5px; left: -11px; width: 12px; z-index: 700; } - -.tip_content { height: auto !important; height: 20px; min-height: 20px; padding: 10px 5px 5px 5px; border: 1px solid #666; background: #fff; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; z-index: 500; position: absolute; top: 0; left: -1px; width: 300px; } - -.tip_close { position: absolute; text-decoration: none; left: 100%; top: 0; margin-left: -12px; } +.tip_box { + display:block; + height:30px; + position:absolute; + z-index:1000; +} + +.tip_arrow { + display:block; + position:absolute; + top:5px; + left:-12px; + width:12px; + z-index:700; +} + +.tip_box.right .tip_arrow { + top: 5px; + right: -12px; + left: auto; +} + +.flip-x { + -moz-transform: scaleX(-1); + -o-transform: scaleX(-1); + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + filter: FlipH; + -ms-filter: "FlipH"; +} + +.tip_content { + height:auto !important; + height:20px; + min-height:20px; + padding:10px; + border:1px solid #666; + background:#fff; + -moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px; + -moz-box-shadow: 3px 3px 3px #666; + -webkit-box-shadow: 3px 3px 3px #666; + box-shadow: 3px 3px 3px #666; + z-index:500; + position:absolute; + top:0; + left:-1px; + min-width:300px; + line-height: 1.15rem; +} + +.tip_content .links { + margin-top: 0.7em; + padding-top: 0.4em; + border-top: 1px solid #ddd; +} + +.tip_content .links a { + color: #548dd4; +} + +.tip_content hr { + + color: #ddd; + background-color: #ddd; + height: 1px; + border: 0; + padding: 0; + margin: 0.2em 0; + width: 100%; +} + +.tip_close { + position:absolute; + top:0.3em; + right:0.5em; + text-decoration:none; +} + +.tip_shadow { + display:none; + background:#000; + filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.60); + -ms-filter: "progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.60)"; + zoom: 1; + position:absolute; + z-index:200; + top:0; + left:0; + width:auto !important; + width:310px; +} + +.tip_content h1 { + font-size: 13pt; + margin-top: 0; + margin-bottom: 0.5em; + padding-bottom: 0.2em; + border-bottom: 1px solid #ddd; + padding-right: 1.5em; +} + +i.help-tip { + vertical-align: inherit; + color: #aaa; +} +i.help-tip:hover { + color: black; + cursor: pointer; +} #overlay { display: none; position: fixed; background: #000; z-index: 2000; } diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index b735e031afbf9f978524ac71651d0d770d814743..d57fa0b449095e9bb88a1cb3b9004dd0a1325c5a 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -205,7 +205,7 @@ class Installer extends SetupWizard { 'default_dept_id'=>$dept_id_1, 'default_sla_id'=>$sla_id_1, 'default_template_id'=>$template_id_1, 'admin_email'=>$vars['admin_email'], - 'schema_signature'=>$signature, + 'schema_signature'=>$streams['core'], 'helpdesk_url'=>URL, 'helpdesk_title'=>$vars['name']); $config = new Config('core'); @@ -217,6 +217,14 @@ class Installer extends SetupWizard { $company = new Company(); $company->getForm()->setAnswer('name', $vars['name']); $company->getForm()->save(); + + foreach ($streams as $stream=>$signature) { + if ($stream != 'core') { + $config = new Config($stream); + if (!$config->update('schema_signature', $signature)) + $this->errors['err']='Unable to create config settings (#8)'; + } + } } if($this->errors) return false; //Abort on internal errors. diff --git a/setup/inc/install-prereq.inc.php b/setup/inc/install-prereq.inc.php index 37579a32a1ac976e0800511612f29029e1f23be3..90e0f176ac0f0cb138b136a2e76ff16427f73898 100644 --- a/setup/inc/install-prereq.inc.php +++ b/setup/inc/install-prereq.inc.php @@ -17,7 +17,7 @@ if(!defined('SETUPINC')) die('Kwaheri!'); <li class="<?php echo $installer->check_php()?'yes':'no'; ?>"> PHP v5.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li> <li class="<?php echo $installer->check_mysql()?'yes':'no'; ?>"> - MySQL v5.0 or greater - (<small><b><?php echo extension_loaded('mysql')?'module loaded':'missing!'; ?></b></small>)</li> + MySQLi extension for PHP - (<small><b><?php echo extension_loaded('mysqli')?'module loaded':'missing!'; ?></b></small>)</li> </ul> <h3>Recommended:</h3> You can use osTicket without these, but you may not be able to use all features. diff --git a/setup/inc/install.inc.php b/setup/inc/install.inc.php index c9f16384398a75e5d6c919b2fdf08be948ff7189..e2d6a5a0f3b87b28f7466a91780b2d5d932956fb 100644 --- a/setup/inc/install.inc.php +++ b/setup/inc/install.inc.php @@ -17,13 +17,13 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho <div class="row"> <label>Helpdesk Name:</label> <input type="text" name="name" size="45" tabindex="1" value="<?php echo $info['name']; ?>"> - <a class="tip" href="#helpdesk_name">?</a> + <a class="tip" href="#helpdesk_name"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['name']; ?></font> </div> <div class="row"> <label>Default Email:</label> <input type="text" name="email" size="45" tabindex="2" value="<?php echo $info['email']; ?>"> - <a class="tip" href="#system_email">?</a> + <a class="tip" href="#system_email"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['email']; ?></font> </div> @@ -32,37 +32,37 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho <div class="row"> <label>First Name:</label> <input type="text" name="fname" size="45" tabindex="3" value="<?php echo $info['fname']; ?>"> - <a class="tip" href="#first_name">?</a> + <a class="tip" href="#first_name"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['fname']; ?></font> </div> <div class="row"> <label>Last Name:</label> <input type="text" name="lname" size="45" tabindex="4" value="<?php echo $info['lname']; ?>"> - <a class="tip" href="#last_name">?</a> + <a class="tip" href="#last_name"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['lname']; ?></font> </div> <div class="row"> <label>Email Address:</label> <input type="text" name="admin_email" size="45" tabindex="5" value="<?php echo $info['admin_email']; ?>"> - <a class="tip" href="#email">?</a> + <a class="tip" href="#email"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['admin_email']; ?></font> </div> <div class="row"> <label>Username:</label> <input type="text" name="username" size="45" tabindex="6" value="<?php echo $info['username']; ?>" autocomplete="off"> - <a class="tip" href="#username">?</a> + <a class="tip" href="#username"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['username']; ?></font> </div> <div class="row"> <label> Password:</label> <input type="password" name="passwd" size="45" tabindex="7" value="<?php echo $info['passwd']; ?>" autocomplete="off"> - <a class="tip" href="#password">?</a> + <a class="tip" href="#password"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['passwd']; ?></font> </div> <div class="row"> <label>Retype Password:</label> <input type="password" name="passwd2" size="45" tabindex="8" value="<?php echo $info['passwd2']; ?>"> - <a class="tip" href="#password2">?</a> + <a class="tip" href="#password2"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['passwd2']; ?></font> </div> @@ -71,31 +71,31 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho <div class="row"> <label>MySQL Table Prefix:</label> <input type="text" name="prefix" size="45" tabindex="9" value="<?php echo $info['prefix']; ?>"> - <a class="tip" href="#db_prefix">?</a> + <a class="tip" href="#db_prefix"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['prefix']; ?></font> </div> <div class="row"> <label>MySQL Hostname:</label> <input type="text" name="dbhost" size="45" tabindex="10" value="<?php echo $info['dbhost']; ?>"> - <a class="tip" href="#db_host">?</a> + <a class="tip" href="#db_host"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['dbhost']; ?></font> </div> <div class="row"> <label>MySQL Database:</label> <input type="text" name="dbname" size="45" tabindex="11" value="<?php echo $info['dbname']; ?>"> - <a class="tip" href="#db_schema">?</a> + <a class="tip" href="#db_schema"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['dbname']; ?></font> </div> <div class="row"> <label>MySQL Username:</label> <input type="text" name="dbuser" size="45" tabindex="12" value="<?php echo $info['dbuser']; ?>"> - <a class="tip" href="#db_user">?</a> + <a class="tip" href="#db_user"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['dbuser']; ?></font> </div> <div class="row"> <label>MySQL Password:</label> <input type="password" name="dbpass" size="45" tabindex="13" value="<?php echo $info['dbpass']; ?>"> - <a class="tip" href="#db_password">?</a> + <a class="tip" href="#db_password"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['dbpass']; ?></font> </div> <br> diff --git a/setup/js/tips.js b/setup/js/tips.js index 9fd2007e5a67cd2e98b85b3ef1df81deb092dfb4..af111e21ed7171f4e33b4f45256fc09be29d70d0 100644 --- a/setup/js/tips.js +++ b/setup/js/tips.js @@ -20,54 +20,69 @@ jQuery(function($) { }) .live('mouseover click', function(e) { e.preventDefault(); - var tip_num = this.rel; - if($('.' + tip_num).length == 0) { + var elem = $(this), + pos = elem.offset(), + y_pos = pos.top - 8, + x_pos = pos.left + elem.width() + 16, + tip_arrow = $('<img>') + .attr('src', './images/tip_arrow.png') + .addClass('tip_arrow'), + tip_box = $('<div>') + .addClass('tip_box'), + tip_content = $('<div>') + .append('<a href="#" class="tip_close"><i class="icon-remove-circle"></i></a>') + .addClass('tip_content'), + the_tip = tip_box + .append(tip_content.append(tip_arrow)) + .css({ + "top":y_pos + "px", + "left":x_pos + "px" + }), + tip_timer = setTimeout(function() { + $('.tip_box').remove(); + $('body').append(the_tip.hide().fadeIn()); + if ($(window).width() < tip_content.outerWidth() + the_tip.position().left) { + the_tip.css({'left':x_pos-tip_content.outerWidth()-40+'px'}); + tip_box.addClass('right'); + tip_arrow.addClass('flip-x'); + } + }, 500); - var elem = $(this), - pos = elem.offset(), - y_pos = pos.top - 12, - x_pos = pos.left + elem.width() + 20, - tip_arrow = $('<img>') - .attr('src', './images/tip_arrow.png') - .addClass('tip_arrow'), - tip_box = $('<div>') - .addClass('tip_box'), - tip_content = $('<div>') - .append('<a href="#" class="tip_close">x</a>') - .addClass('tip_content'), - the_tip = tip_box - .append(tip_arrow) - .append(tip_content) - .css({ - "top":y_pos + "px", - "left":x_pos + "px" - }) - .addClass(tip_num), - tip_timer = setTimeout(function() { - $('.tip_box').remove(); - $('body').append(the_tip.hide().fadeIn()); - }, 500); + elem.live('mouseout', function() { + clearTimeout(tip_timer); + }); - elem.live('mouseout', function() { + getHelpTips().then(function(tips) { + var section = tips[elem.attr('href').substr(1)]; + if (!section) { + elem.remove(); clearTimeout(tip_timer); - }); - - getHelpTips().then(function(tips) { - var section = tips[elem.attr('href').substr(1)]; - if (!section) { - elem.remove(); - clearTimeout(tip_timer); - return; - } - tip_content.append( - $('<b>').append(section.title)) - .append(section.content); - }); - $('.' + tip_num + ' .tip_shadow').css({ - "height":$('.' + tip_num).height() + 5 - }); - } + return; + } + tip_content.append( + $('<h1>') + .append('<i class="icon-info-sign faded"> ') + .append(section.title) + ).append(section.content); + if (section.links) { + var links = $('<div class="links">'); + $.each(section.links, function(i,l) { + var icon = l.href.match(/^http/) + ? 'icon-external-link' : 'icon-share-alt'; + links.append($('<div>') + .append($('<a>') + .html(l.title) + .prepend('<i class="'+icon+'"></i> ') + .attr('href', l.href).attr('target','_blank')) + ); + }); + tip_content.append(links); + } + }); + $('.tip_shadow', the_tip).css({ + "height":the_tip.height() + 5 + }); }); $('body')