diff --git a/README.md b/README.md index 9946aeb3d85e6c3715b57f9347d10ccdc4edb244..f09b94d050780ec31d5953b61118b417edb6c6eb 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,20 @@ Create your own fork of the project and use [git-flow](https://github.com/nvie/gitflow) to create a new feature. Once the feature is published in your fork, send a pull request to begin the conversation of integrating your new feature into osTicket. + +License +------- +osTicket is released under the GPL2 license. See the included LICENSE.txt +file for the gory details of the General Public License. + +osTicket is supported by several magical open source projects including: + + * [HTMLawed](http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed) + * [PasswordHash](http://www.openwall.com/phpass/) + * [PEAR](http://pear.php.net/package/PEAR) + * [PEAR/Auth_SASL](http://pear.php.net/package/Auth_SASL) + * [PEAR/Mail](http://pear.php.net/package/mail) + * [PEAR/Net_SMTP](http://pear.php.net/package/Net_SMTP) + * [PEAR/Net_Socket](http://pear.php.net/package/Net_Socket) + * [PEAR/Serivces_JSON](http://pear.php.net/package/Services_JSON) + * [phplint](http://antirez.com/page/phplint.html) diff --git a/WHATSNEW.md b/WHATSNEW.md index 522bc605e475837fe6c321c75a57d2fe086880dd..ed656f683a7c03dc708939991227ef9829dbd270 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -1,3 +1,7 @@ +New stuff in 1.7-dpr3 +====================== + + New stuff in 1.7-dpr2 ====================== diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css index c6a6242df3d461805e06d366f451d3abf34a29ff..3f1cb699c0c6d477b45bec4a4e08b2626b75ade4 100644 --- a/assets/default/css/theme.css +++ b/assets/default/css/theme.css @@ -254,6 +254,9 @@ body { height: 20px; background: url('../images/nav_bg.png') top left repeat-x; border-top: 1px solid #aaa; + box-shadow:0 3px 2px rgba(0, 0, 0, 0.4); + -moz-box-shadow:0 3px 2px rgba(0, 0, 0, 0.4); + -webkit-box-shadow:0 3px 2px rgba(0, 0, 0, 0.4); } #nav li { margin: 0; @@ -304,19 +307,11 @@ body { #content { padding: 20px 0; margin: 0 20px; - background: url('../images/content_bg.png') top left repeat-x; -} - -#cnbg { - padding: 5px 0; - margin: 0 20px; height: auto !important; height: 350px; min-height: 350px; - background: none; } - #footer { text-align: center; font-size: 11px; @@ -384,19 +379,33 @@ body { font-size: 15px; margin-left: 0; padding-left: 0; + border-top:1px solid #ddd; } #faq ol li { list-style: none; - margin: 0 0 10px 0; + margin: 0; + padding:0; color: #999; } #faq ol li a { - display: block; - height: 16px; + display:block; + padding:5px 0; + height:auto !important; + overflow:hidden; + margin:0; + border-bottom:1px solid #ddd; line-height: 16px; padding-left: 24px; background: url('../images/icons/page.png?1319579499') 0 50% no-repeat; } +#faq ol li a:hover { + background-color:#e9f5ff; +} + +.article-meta { + padding:5px; + background:#fafafa; +} /* Knowledgebase */ #kb { @@ -406,12 +415,21 @@ body { } #kb > li { - margin: 0 0 5px 0; - padding: 10px; - width: auto; - float: left; - clear: both; - list-style: none; + padding:10px; + height:auto !important; + overflow:hidden; + margin:0; + background:url(../images/kb_category_bg.png) bottom left repeat-x; + border-bottom:1px solid #ddd; +} + +#kb li i { + display:block; + width:32px; + height:32px; + float:left; + margin-right:6px; + background:url(../images/kb_large_folder.png) top left no-repeat; } #kb > li h4 { @@ -425,10 +443,51 @@ body { #kb > li h4 a { font-size: 14px; - padding-left: 24px; - background: url('../images/icons/page.png?1319579499') 0 50% no-repeat; } +#kb-search { + padding:10px 0; + overflow:hidden; +} + +#kb-search div { + clear:both; + overflow:hidden; + padding-top:5px; +} + +#kb-search #query { + margin:0; + display:inline-block; + float:left; + width:200px; + margin-right:5px; +} + +#kb-search #cid { + margin:0; + display:inline-block; + float:left; + width:200px; + margin-right:5px; + position:relative; + top:2px; +} + +#kb-search #topic-id { + margin:0; + display:inline-block; + float:left; + width:410px; +} + +#kb-search #searchSubmit { + margin:0; + display:inline-block; + float:left; + position:relative; + top:2px; +} #breadcrumbs { color: #333; @@ -707,3 +766,35 @@ a.refresh { margin-right: 20px; background: url('../images/icons/file.gif') 0 50% no-repeat; } + +.button, .button:visited { + background: #222; + display: inline-block; + font-size: 16px; + padding: 8px 16px 6px 16px; + width:160px; + text-align:center; + color: #fff; + font-weight:bold; + text-decoration: none; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + box-shadow: 0 1px 3px rgba(0,0,0,0.5); + -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.5); + -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5); + text-shadow: 0 -1px 1px rgba(0,0,0,0.25); + border-bottom: 1px solid rgba(0,0,0,0.25); + position: relative; + cursor: pointer; + font-family:helvetica, arial, sans-serif; +} + +.button:hover { background-color: #111; color: #fff; } +.button:active { top: 1px; box-shadow:none; -moz-box-shadow:none; -webkit-box-shadow:none; } +.button, .button:visited, +.green.button, .green.button:visited { background-color: #91bd09; } +.green.button:hover { background-color: #749a02; } +.blue.button, .blue.button:visited { background-color: #00AEEF; } +.blue.button:hover { background-color: #0299d2; } + diff --git a/assets/default/images/kb_category_bg.png b/assets/default/images/kb_category_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..174d675f4c491060a413232bdef7e0e9298df60e Binary files /dev/null and b/assets/default/images/kb_category_bg.png differ diff --git a/assets/default/images/kb_large_folder.png b/assets/default/images/kb_large_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..144fa828a3fc83613dd7a5b29368b6da290fd09d Binary files /dev/null and b/assets/default/images/kb_large_folder.png differ diff --git a/client.inc.php b/client.inc.php index b714e93b44bd587f4d10a2f292b2d4890bc63a3f..3b450e9e2491653ec44c65f346ef79758cc1d1a8 100644 --- a/client.inc.php +++ b/client.inc.php @@ -28,21 +28,12 @@ define('OSTCLIENTINC',TRUE); define('ASSETS_PATH',ROOT_PATH.'assets/default/'); - //Check the status of the HelpDesk. -if(!is_object($cfg) || !$cfg->getId() || $cfg->isHelpDeskOffline()) { +if(!is_object($cfg) || !$cfg->getId() || $cfg->isHelpDeskOffline() || $cfg->isUpgradePending()) { include('./offline.php'); exit; } -//Forced upgrade? Version mismatch. -if(defined('THIS_VERSION') && strcasecmp($cfg->getVersion(),THIS_VERSION)) { - die('System is offline for an upgrade.'); - exit; -} - - - /* include what is needed on client stuff */ require_once(INCLUDE_DIR.'class.client.php'); require_once(INCLUDE_DIR.'class.ticket.php'); diff --git a/include/ajax.config.php b/include/ajax.config.php index 7dcfd9972717e9c8cd5cb14a217b0e3b361dd9ee..fc9fb2c3fc8622108054052828b7e6bb9a42dcd6 100644 --- a/include/ajax.config.php +++ b/include/ajax.config.php @@ -19,7 +19,7 @@ if(!defined('INCLUDE_DIR')) die('!'); class ConfigAjaxAPI extends AjaxController { //config info UI might need. - function ui() { + function scp_ui() { global $thisstaff, $cfg; $config=array('ticket_lock_time'=>($cfg->getLockTime()*3600), diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 458e430ea83192703bea52c026413af63c4edb63..f67581005194848131dbaf1c709b8c238c8e404a 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -20,11 +20,11 @@ include_once(INCLUDE_DIR.'class.ticket.php'); class TicketsAjaxAPI extends AjaxController { - function search() { + function lookup() { global $thisstaff; if(!is_numeric($_REQUEST['q'])) - return self::searchByEmail(); + return self::lookupByEmail(); $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; @@ -53,7 +53,7 @@ class TicketsAjaxAPI extends AjaxController { return $this->json_encode($tickets); } - function searchByEmail() { + function lookupByEmail() { global $thisstaff; @@ -84,6 +84,100 @@ class TicketsAjaxAPI extends AjaxController { return $this->json_encode($tickets); } + function search() { + global $thisstaff; + + $result=array(); + $select = 'SELECT count(ticket.ticket_id) as tickets '; + $from = ' FROM '.TICKET_TABLE.' ticket '; + $where = ' WHERE 1 '; + + //Access control. + $where.=' AND ( ticket.staff_id='.db_input($thisstaff->getId()); + + if(($teams=$thisstaff->getTeams()) && count(array_filter($teams))) + $where.=' OR ticket.team_id IN('.implode(',', array_filter($teams)).')'; + + if(!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) + $where.=' OR ticket.dept_id IN ('.implode(',', $depts).')'; + + $where.=' ) '; + + //Department + if($_REQUEST['deptId']) + $where.=' AND ticket.dept_id='.db_input($_REQUEST['deptId']); + + //Status + switch(strtolower($_REQUEST['status'])) { + case 'open'; + $where.=' AND ticket.status="open" '; + break; + case 'overdue': + $where.=' AND ticket.status="open" AND ticket.isoverdue=1 '; + break; + case 'closed': + $where.=' AND ticket.status="closed" '; + break; + } + + //Assignee + if($_REQUEST['assignee'] && strcasecmp($_REQUEST['status'], 'closed')) { + $id=preg_replace("/[^0-9]/", "", $_REQUEST['assignee']); + $assignee = $_REQUEST['assignee']; + $where.= ' AND ( '; + if($assignee[0]=='t') + $where.=' (ticket.team_id='.db_input($id). ' AND ticket.status="open") '; + elseif($assignee[0]=='s') + $where.=' (ticket.staff_id='.db_input($id). ' AND ticket.status="open") '; + else + $where.=' (ticket.staff_id='.db_input($id). ' AND ticket.status="open") '; + + if($_REQUEST['staffId'] && !$_REQUEST['status']) //Assigned TO + Closed By + $where.= ' OR (ticket.staff_id='.db_input($_REQUEST['staffId']). ' AND ticket.status="closed") '; + + $where.= ' ) '; + } elseif($_REQUEST['staffId']) { + $where.=' AND (ticket.staff_id='.db_input($_REQUEST['staffId']).' AND ticket.status="closed") '; + } + + //dates + $startTime =($_REQUEST['startDate'] && (strlen($_REQUEST['startDate'])>=8))?strtotime($_REQUEST['startDate']):0; + $endTime =($_REQUEST['endDate'] && (strlen($_REQUEST['endDate'])>=8))?strtotime($_REQUEST['endDate']):0; + if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0)) + $startTime=$endTime=0; + + if($startTime) + $where.=' AND ticket.created>=FROM_UNIXTIME('.$startTime.')'; + + if($endTime) + $where.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')'; + + //Query + if($_REQUEST['query']) { + $queryterm=db_real_escape($_REQUEST['query'], false); + + $from.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'; + $where.=" AND ( ticket.email LIKE '%$queryterm%'" + ." OR ticket.name LIKE '%$queryterm%'" + ." OR ticket.subject LIKE '%$queryterm%'" + ." OR thread.title LIKE '%$queryterm%'" + ." OR thread.body LIKE '%$queryterm%'" + .' )'; + $groupby = 'GROUP BY ticket.ticket_id '; + } + + $sql="$select $from $where $groupby"; + if(($tickets=db_result(db_query($sql)))) { + $result['success'] =sprintf("Search criteria matched %s - <a href='tickets.php?%s'>view</a>", + ($tickets>1?"$tickets tickets":"$tickets ticket"), + str_replace(array('&', '&'), array('&', '&'), $_SERVER['QUERY_STRING'])); + } else { + $result['fail']='No tickets found matching your search criteria.'; + } + + return $this->json_encode($result); + } + function acquireLock($tid) { global $cfg,$thisstaff; diff --git a/include/class.attachment.php b/include/class.attachment.php index 8644d671c4986f6b5541e6b9b592fa06fde91652..442c9865e495d8e6d32e380f712524b66c5ef0e6 100644 --- a/include/class.attachment.php +++ b/include/class.attachment.php @@ -93,10 +93,12 @@ class Attachment { return db_result(db_query($sql)); } - function lookup($id,$tid=0) { - $id=is_numeric($id)?$id:self::getIdByFileHash($hash,$tid); + function lookup($var,$tid=0) { + $id=is_numeric($var)?$var:self::getIdByFileHash($var,$tid); - return ($id && is_numeric($id) && ($attach = new Attachment($id,$tid)) && $attach->getId()==$id)?$attach:null; + return ($id && is_numeric($id) + && ($attach = new Attachment($id,$tid)) + && $attach->getId()==$id)?$attach:null; } } diff --git a/include/class.canned.php b/include/class.canned.php index 6c6c1ac1c75bcbc25b7c288c2e49909e30b4876a..51d39ca48024410bdefecba03f20b7b030f30649 100644 --- a/include/class.canned.php +++ b/include/class.canned.php @@ -130,6 +130,7 @@ class Canned { */ function uploadAttachments($files) { + $i=0; foreach($files as $file) { if(($fileId=is_numeric($file)?$file:AttachmentFile::upload($file)) && is_numeric($fileId)) { $sql ='INSERT INTO '.CANNED_ATTACHMENT_TABLE @@ -174,7 +175,7 @@ class Canned { return self::save(0,$vars,$errors); } - function getIdByTitle($titke) { + function getIdByTitle($title) { $sql='SELECT canned_id FROM '.CANNED_TABLE.' WHERE title='.db_input($title); if(($res=db_query($sql)) && db_num_rows($res)) list($id)=db_fetch_row($res); diff --git a/include/class.config.php b/include/class.config.php index c51814bebc73aef1ae3a9052db364d2702798c04..95a5204e389b55c161cf192c1af9511793732e70 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -67,22 +67,35 @@ class Config { return !$this->isSystemOnline(); } + function isHelpDeskOnline() { + return $this->isSystemOnline(); + } + function isSystemOnline() { - return ($this->config['isonline']); + return ($this->config['isonline'] && !$this->isUpgradePending()); } - function isKnowledgebaseEnabled() { + function isUpgradePending() { + return (defined('SCHEMA_SIGNATURE') && strcasecmp($this->getSchemaSignature(), SCHEMA_SIGNATURE)); + } + function isKnowledgebaseEnabled() { require_once(INCLUDE_DIR.'class.faq.php'); return ($this->config['enable_kb'] && FAQ::countPublishedFAQs()); } function getVersion() { - return '1.7-DPR2'; + return THIS_VERSION; } function getSchemaSignature() { - return $this->config['schema_signature']; + + if($this->config['schema_signature']) + return $this->config['schema_signature']; + elseif($this->config['ostversion']) //old version 1.6 st. + return md5(strtoupper($this->config['ostversion'])); + + return null; } function setMysqlTZ($tz) { diff --git a/include/class.dept.php b/include/class.dept.php index 8bd575116a421dba88b82dd236243829aeadfc32..bf82b76526b7ce77abbdcdbaa213c939207ca4ad 100644 --- a/include/class.dept.php +++ b/include/class.dept.php @@ -47,6 +47,7 @@ class Dept { $this->id=$this->ht['dept_id']; $this->email=$this->sla=$this->manager=null; $this->getEmail(); //Auto load email struct. + $this->members=array(); return true; } @@ -88,6 +89,21 @@ class Dept { return $this->getNumStaff(); } + function getAvailableMembers(){ + + if(!$this->members && $this->getNumStaff()){ + $sql='SELECT m.staff_id FROM '.STAFF_TABLE.' m ' + .'WHERE m.dept_id='.db_input($this->getId()) + .' AND s.staff_id IS NOT NULL ' + .'ORDER BY s.lastname, s.firstname'; + if(($res=db_query($sql)) && db_num_rows($res)){ + while(list($id)=db_fetch_row($res)) + if($staff= Staff::lookup($id) && $staff->isAvailable()) + $this->members[]= $staff; + } + } + return $this->members; + } function getSLAId(){ return $this->ht['sla_id']; diff --git a/include/class.faq.php b/include/class.faq.php index 7e23803dc458272ed7f81184c190213ddabd0e85..b0fdc4ac2b0890895f0adfa3a3268f5e63531221 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -127,7 +127,7 @@ class FAQ { /* Same as update - but mainly called after one or more setters are changed. */ function apply() { //XXX: set errors and add ->getErrors() & ->getError() - return $this->update($this->ht, $errors); + return $this->update($this->ht, $errors); # nolint } function updateTopics($ids){ @@ -204,6 +204,7 @@ class FAQ { function uploadAttachments($files) { + $i=0; foreach($files as $file) { if(($fileId=is_numeric($file)?$file:AttachmentFile::upload($file)) && is_numeric($fileId)) { $sql ='INSERT INTO '.FAQ_ATTACHMENT_TABLE diff --git a/include/class.filter.php b/include/class.filter.php index 4f18e96514c307077b7300a02674f8ad1698039e..9c3065c10aba5aa1e359e4c36a67e4de83aec9fe 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -156,7 +156,7 @@ class Filter { $rule= array_merge($extra,array('w'=>$what, 'h'=>$how, 'v'=>$val)); $rule['filter_id']=$this->getId(); - return FilterRule::create($rule,$errors); + return FilterRule::create($rule,$errors); # nolint } function removeRule($what, $how, $val) { @@ -419,7 +419,8 @@ class Filter { if($errors || !$id) return false; //Success with update/create...save the rules. We can't recover from any errors at this point. - self::save_rules($id,$vars,$xerrors); + # Don't care about errors stashed in $xerrors + self::save_rules($id,$vars,$xerrors); # nolint return true; } diff --git a/include/class.format.php b/include/class.format.php index 45f4301913072fa49d93d7dc3a10019b82536563..a3461bf9d615c857382763c62b8ff49562352648 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -104,7 +104,11 @@ class Format { $text=Format::clickableurls($text); //Wrap long words... - $text=preg_replace_callback('/\w{75,}/',create_function('$matches','return wordwrap($matches[0],70,"\n",true);'),$text); + $text=preg_replace_callback('/\w{75,}/', + create_function( + '$matches', # nolint + 'return wordwrap($matches[0],70,"\n",true);'), # nolint + $text); return nl2br($text); } @@ -122,7 +126,7 @@ class Format { $text=preg_replace("/(^|[ \\n\\r\\t])(www\.([a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+)(\/[^\/ \\n\\r]*)*)/", '\\1<a href="l.php?url=http://\\2" target="_blank">\\2</a>', $text); $text=preg_replace("/(^|[ \\n\\r\\t])([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,4})/", - '\\1<a href="l.php?url=mailto:\\2" target="_blank">\\2</a>', $text); + '\\1<a href="mailto:\\2" target="_blank">\\2</a>', $text); return $text; } diff --git a/include/class.knowledgebase.php b/include/class.knowledgebase.php index d8a64689168de48014011bf46c45e029056efdea..6bb67898f7a94f7b6f95c547b646241aa45c2699 100644 --- a/include/class.knowledgebase.php +++ b/include/class.knowledgebase.php @@ -55,9 +55,11 @@ class Knowledgebase { function publish() { $this->published = true; } function unpublish() { $this->published = false; } function setPublished($val) { $this->published = !!$val; } + function setEnabled($val) { $this->enabled = !!$val; } function setTitle($title) { $this->title = $title; } function setKeywords($words) { $this->keywords = $words; } function setAnswer($text) { $this->answer = $text; } + function setDepartment($id) { $this->department = $id; } /* -------------> Validation and Clean methods <------------ */ function validate(&$errors, $what=null) { diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index 5f97775930f25da87f80261cc20b66cdad947b8f..4ee15ecca38404efcd943fcd984d6b3d335854b6 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -242,7 +242,7 @@ class MailFetcher { function getBody($mid) { $body =''; - if(!($body = $this->getpart($mid,'TEXT/PLAIN',$this->charset))) { + if(!($body = $this->getPart($mid,'TEXT/PLAIN',$this->charset))) { if(($body = $this->getPart($mid,'TEXT/HTML',$this->charset))) { //Convert tags of interest before we striptags $body=str_replace("</DIV><DIV>", "\n", $body); diff --git a/include/class.mailparse.php b/include/class.mailparse.php index 88648367615c7141e2c87b4534f3df683d4ef081..f56006fa9c1e35c88d67165302a7797f43f562d7 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -50,8 +50,10 @@ class Mail_Parse { function splitBodyHeader() { - if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s",$this->mime_message, $match)) { - $this->header=$match[1]; + if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", + $this->mime_message, + $match)) { # nolint + $this->header=$match[1]; # nolint } } /** diff --git a/include/class.nav.php b/include/class.nav.php index a3b719f08d6bec16f6a2a7bb3fb264b1730c2f13..ce3623d37acccb57d0eae500360a5579789683b5 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -42,16 +42,16 @@ class StaffNav { function isStaffPanel() { return (!$this->isAdminPanel()); } - + function setTabActive($tab){ - + if($this->tabs[$tab]){ $this->tabs[$tab]['active']=true; if($this->activetab && $this->activetab!=$tab && $this->tabs[$this->activetab]) $this->tabs[$this->activetab]['active']=false; $this->activetab=$tab; - + return true; } @@ -73,9 +73,9 @@ class StaffNav { function getActiveMenu() { return $this->activeMenu; } - + function addSubMenu($item,$active=false){ - + $this->submenus[$this->getPanel().'.'.$this->activetab][]=$item; if($active) $this->activeMenu=sizeof($this->submenus[$this->getPanel().'.'.$this->activetab]); @@ -105,7 +105,7 @@ class StaffNav { $subnav[]=array('desc'=>'Tickets','href'=>'tickets.php','iconclass'=>'Ticket', 'droponly'=>true); if($staff) { if(($assigned=$staff->getNumAssignedTickets())) - $subnav[]=array('desc'=>"My Tickets ($assigned)", + $subnav[]=array('desc'=>"My Tickets ($assigned)", 'href'=>'tickets.php?status=assigned', 'iconclass'=>'assignedTickets', 'droponly'=>true); @@ -123,19 +123,19 @@ class StaffNav { $subnav[]=array('desc'=>'My Profile','href'=>'profile.php','iconclass'=>'users'); break; case 'kbase': - $subnav[]=array('desc'=>'Knowledgebase','href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'premade'); + $subnav[]=array('desc'=>'Knowledgebase','href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'kb'); if($staff) { if($staff->canManageFAQ()) - $subnav[]=array('desc'=>'Categories','href'=>'categories.php','iconclass'=>'premade'); + $subnav[]=array('desc'=>'Categories','href'=>'categories.php','iconclass'=>'kb-categories'); if($staff->canManageCannedResponses()) - $subnav[]=array('desc'=>'Canned Replies','href'=>'canned.php','iconclass'=>'premade'); + $subnav[]=array('desc'=>'Canned Replies','href'=>'canned.php','iconclass'=>'canned'); } break; } if($subnav) $submenus[$this->getPanel().'.'.strtolower($k)]=$subnav; } - + return $submenus; } @@ -170,7 +170,7 @@ class AdminNav extends StaffNav{ $tabs['depts']=array('desc'=>'Departments','href'=>'departments.php','title'=>'Departments'); $this->tabs=$tabs; } - + return $this->tabs; } @@ -226,12 +226,12 @@ class AdminNav extends StaffNav{ } class UserNav { - + var $navs=array(); var $activenav; var $user; - + function UserNav($user=null, $active=''){ $this->user=$user; @@ -257,7 +257,7 @@ class UserNav { function getNavLinks(){ global $cfg; - + //Paths are based on the root dir. if(!$this->navs){ diff --git a/include/class.pagenate.php b/include/class.pagenate.php index b182db517e579a9a7f4921a086e57825cea026a5..baad19b09fc6012fe352b5d7b4da3a251b73ee6f 100644 --- a/include/class.pagenate.php +++ b/include/class.pagenate.php @@ -15,37 +15,37 @@ **********************************************************************/ class PageNate { - var $start; - var $limit; - var $total; - var $page; - var $pages; - + var $start; + var $limit; + var $total; + var $page; + var $pages; + - function PageNate($total,$page,$limit=20,$url='') { - $this->total = intval($total); - $this->limit = max($limit, 1 ); - $this->page = max($page, 1 ); - $this->start = max((($page-1)*$this->limit),0); - $this->pages = ceil( $this->total / $this->limit ); - - if (($this->limit > $this->total) || ($this->page>ceil($this->total/$this->limit))) { - $this->start = 0; - } - if (($this->limit-1)*$this->start > $this->total) { - $this->start -= $this->start % $this->limit; - } - $this->setURL($url); - } - function setURL($url='',$vars=''){ - if($url){ - if(strpos($url,'?')===false) - $url=$url.'?'; - }else{ - $url=THISPAGE.'?'; - } - $this->url=$url.$vars; - } + function PageNate($total,$page,$limit=20,$url='') { + $this->total = intval($total); + $this->limit = max($limit, 1 ); + $this->page = max($page, 1 ); + $this->start = max((($page-1)*$this->limit),0); + $this->pages = ceil( $this->total / $this->limit ); + + if (($this->limit > $this->total) || ($this->page>ceil($this->total/$this->limit))) { + $this->start = 0; + } + if (($this->limit-1)*$this->start > $this->total) { + $this->start -= $this->start % $this->limit; + } + $this->setURL($url); + } + function setURL($url='',$vars=''){ + if($url){ + if(strpos($url,'?')===false) + $url=$url.'?'; + }else{ + $url=THISPAGE.'?'; + } + $this->url=$url.$vars; + } function getStart() { return $this->start; @@ -64,34 +64,34 @@ class PageNate { return ceil(($this->start+1)/$this->limit); } - function showing() { - $html = ''; - $from= $this->start+1; - if ($this->start + $this->limit < $this->total) { - $to= $this->start + $this->limit; - } else { - $to= $this->total; - } + function showing() { + $html = ''; + $from= $this->start+1; + if ($this->start + $this->limit < $this->total) { + $to= $this->start + $this->limit; + } else { + $to= $this->total; + } $html=" Showing "; - if ($this->total > 0) { + if ($this->total > 0) { $html .= "$from - $to of " .$this->total; - }else{ - $html .= " 0 "; - } - return $html; - } + }else{ + $html .= " 0 "; + } + return $html; + } - function getPageLinks() { - $html = ''; - $file =$this->url; - $displayed_span = 5; - $total_pages = ceil( $this->total / $this->limit ); - $this_page = ceil( ($this->start+1) / $this->limit ); + function getPageLinks() { + $html = ''; + $file =$this->url; + $displayed_span = 5; + $total_pages = ceil( $this->total / $this->limit ); + $this_page = ceil( ($this->start+1) / $this->limit ); $last=$this_page-1; $next=$this_page+1; - $start_loop = floor($this_page-$displayed_span); + $start_loop = floor($this_page-$displayed_span); $stop_loop = ceil($this_page + $displayed_span); @@ -107,14 +107,14 @@ class PageNate { $html .= "\n<a href=\"$file&p=$lastspan\" ><strong>«</strong></a>"; } - for ($i=$start_loop; $i <= $stop_loop; $i++) { - $page = ($i - 1) * $this->limit; - if ($i == $this_page) { - $html .= "\n<b>[$i]</b>"; - } else { - $html .= "\n<a href=\"$file&p=$i\" ><b>$i</b></a>"; - } - } + for ($i=$start_loop; $i <= $stop_loop; $i++) { + $page = ($i - 1) * $this->limit; + if ($i == $this_page) { + $html .= "\n<b>[$i]</b>"; + } else { + $html .= "\n<a href=\"$file&p=$i\" ><b>$i</b></a>"; + } + } if($stop_loop<$total_pages){ $nextspan=($stop_loop+$displayed_span>$total_pages)?$total_pages-$displayed_span:$stop_loop+$displayed_span; $html .= "\n<a href=\"$file&p=$nextspan\" ><strong>»</strong></a>"; @@ -122,8 +122,8 @@ class PageNate { - return $html; - } + return $html; + } } ?> diff --git a/include/class.staff.php b/include/class.staff.php index ecaaba2adbfb897bbb53b07e44a468eb72374a12..00554157935ba0d82aa77596bf9c912f0fcf5ad5 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -220,7 +220,7 @@ class Staff { return $this->showAssignedOnly(); } - function isadmin() { + function isAdmin() { return ($this->ht['isadmin']); } @@ -261,7 +261,7 @@ class Staff { } function canManageTickets() { - return ($this->isadmin() + return ($this->isAdmin() || $this->canDeleteTickets() || $this->canCloseTickets()); } @@ -495,7 +495,7 @@ class Staff { } function login($username, $passwd, &$errors, $strike=true) { - global $cfg; + global $cfg, $session; if($_SESSION['_staff']['laststrike']) { @@ -552,7 +552,7 @@ class Staff { } function create($vars, &$errors) { - if(($id=self::save(0, $vars, $errors)) && $vars['teams'] && ($self=Staff::lookup($id))) + if(($id=self::save(0, $vars, $errors)) && $vars['teams'] && ($staff=Staff::lookup($id))) $staff->updateTeams($vars['teams']); return $id; @@ -623,6 +623,7 @@ class Staff { .' ,dept_id='.db_input($vars['dept_id']) .' ,group_id='.db_input($vars['group_id']) .' ,timezone_id='.db_input($vars['timezone_id']) + .' ,daylight_saving='.db_input(isset($vars['daylight_saving'])?1:0) .' ,username='.db_input($vars['username']) .' ,firstname='.db_input($vars['firstname']) .' ,lastname='.db_input($vars['lastname']) diff --git a/include/class.sys.php b/include/class.sys.php index ab10cd99442fa640bd36a6a329b6cc27e18f1b7f..33b5c830407f1b56fb1ef8eff47657e1296bb97a 100644 --- a/include/class.sys.php +++ b/include/class.sys.php @@ -96,7 +96,7 @@ class Sys { function purgeLogs(){ global $cfg; - if($cfg && ($gp=$cfg->getLogGraceperiod()) && is_numeric($gp)) { + if($cfg && ($gp=$cfg->getLogGracePeriod()) && is_numeric($gp)) { $sql='DELETE FROM '.SYSLOG_TABLE.' WHERE DATE_ADD(created, INTERVAL '.$gp.' MONTH)<=NOW()'; db_query($sql); } diff --git a/include/class.team.php b/include/class.team.php index 7aada43a3b13a6529822ef8a764ba958b7f838d8..b3f996d28fc658ed03d5e458679fc97953de000b 100644 --- a/include/class.team.php +++ b/include/class.team.php @@ -200,7 +200,6 @@ class Team { $sql='SET updated=NOW(),isenabled='.db_input($vars['isenabled']). ',name='.db_input($vars['name']). - ',isenabled='.db_input($vars['isenabled']). ',noalerts='.db_input(isset($vars['noalerts'])?$vars['noalerts']:0). ',notes='.db_input($vars['notes']); diff --git a/include/class.ticket.php b/include/class.ticket.php index b0f54d2c761461ba987b0e4f91f86ad276d0290e..c1e4cdf23803dd48065dadef0da032130b1283dd 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -75,18 +75,25 @@ class Ticket{ $sql='SELECT ticket.*, topic.topic as helptopic, lock_id, dept_name, priority_desc ' .' ,count(attach.attach_id) as attachments ' - .' ,count(DISTINCT message.msg_id) as messages ' - .' ,count(DISTINCT response.response_id) as responses ' - .' ,count(DISTINCT note.note_id) as notes ' + .' ,count(DISTINCT message.id) as messages ' + .' ,count(DISTINCT response.id) as responses ' + .' ,count(DISTINCT note.id) as notes ' .' FROM '.TICKET_TABLE.' ticket ' .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) ' - .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.priority_id) ' - .' LEFT JOIN '.TOPIC_TABLE.' topic ON (ticket.topic_id=topic.topic_id) ' - .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) ' - .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (ticket.ticket_id=attach.ticket_id) ' - .' LEFT JOIN '.TICKET_MESSAGE_TABLE.' message ON (ticket.ticket_id=message.ticket_id) ' - .' LEFT JOIN '.TICKET_RESPONSE_TABLE.' response ON (ticket.ticket_id=response.ticket_id) ' - .' LEFT JOIN '.TICKET_NOTE_TABLE.' note ON (ticket.ticket_id=note.ticket_id ) ' + .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (' + .'ticket.priority_id=pri.priority_id) ' + .' LEFT JOIN '.TOPIC_TABLE.' topic ON (' + .'ticket.topic_id=topic.topic_id) ' + .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (' + .'ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) ' + .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (' + .'ticket.ticket_id=attach.ticket_id) ' + .' LEFT JOIN '.TICKET_THREAD_TABLE.' message ON (' + ."ticket.ticket_id=message.ticket_id AND message.thread_type = 'M') " + .' LEFT JOIN '.TICKET_THREAD_TABLE.' response ON (' + ."ticket.ticket_id=response.ticket_id AND response.thread_type = 'R') " + .' LEFT JOIN '.TICKET_THREAD_TABLE.' note ON ( ' + ."ticket.ticket_id=note.ticket_id AND note.thread_type = 'N') " .' WHERE ticket.ticket_id='.db_input($id) .' GROUP BY ticket.ticket_id'; @@ -188,7 +195,8 @@ class Ticket{ if(!strcasecmp($client->getEmail(),$this->getEmail())) return true; - return ($cfg && $cfg->showRelatedTickets() && $client->getTicketId()==$ticket->getExtId()); + return ($cfg && $cfg->showRelatedTickets() + && $client->getTicketId()==$this->getExtId()); } //Getters @@ -436,9 +444,10 @@ class Ticket{ function getLastRespondent() { $sql ='SELECT resp.staff_id ' - .' FROM '.TICKET_RESPONSE_TABLE.' resp ' + .' FROM '.TICKET_THREAD_TABLE.' resp ' .' LEFT JOIN '.STAFF_TABLE. ' USING(staff_id) ' .' WHERE resp.ticket_id='.db_input($this->getId()).' AND resp.staff_id>0 ' + .' AND resp.thread_type="R"' .' ORDER BY resp.created DESC LIMIT 1'; if(!($res=db_query($sql)) || !db_num_rows($res)) @@ -456,8 +465,9 @@ class Ticket{ return $this->lastmsgdate; //for old versions...XXX: still needed???? - $sql='SELECT created FROM '.TICKET_MESSAGE_TABLE + $sql='SELECT created FROM '.TICKET_THREAD_TABLE .' WHERE ticket_id='.db_input($this->getId()) + ." AND thread_type = 'M'" .' ORDER BY created DESC LIMIT 1'; if(($res=db_query($sql)) && db_num_rows($res)) list($this->lastmsgdate)=db_fetch_row($res); @@ -474,8 +484,9 @@ class Ticket{ if($this->lastrespdate) return $this->lastrespdate; - $sql='SELECT created FROM '.TICKET_RESPONSE_TABLE + $sql='SELECT created FROM '.TICKET_THREAD_TABLE .' WHERE ticket_id='.db_input($this->getId()) + .' AND thread_type="R"' .' ORDER BY created DESC LIMIT 1'; if(($res=db_query($sql)) && db_num_rows($res)) list($this->lastrespdate)=db_fetch_row($res); @@ -522,11 +533,12 @@ class Ticket{ $order='DESC'; $sql ='SELECT note.*, count(DISTINCT attach.attach_id) as attachments ' - .' FROM '.TICKET_NOTE_TABLE.' note ' + .' FROM '.TICKET_THREAD_TABLE.' note ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach - ON (note.ticket_id=attach.ticket_id AND note.note_id=attach.ref_id AND ref_type="N") ' + ON (note.ticket_id=attach.ticket_id AND note.id=attach.ref_id AND ref_type="N") ' .' WHERE note.ticket_id='.db_input($this->getId()) - .' GROUP BY note.note_id ' + .' AND note.thread_type="N"' + .' GROUP BY note.id ' .' ORDER BY note.created '.$order; $notes=array(); @@ -539,14 +551,17 @@ class Ticket{ function getMessages() { - $sql='SELECT msg.msg_id, msg.created, msg.message ' - .' ,count(DISTINCT attach.attach_id) as attachments, count( DISTINCT resp.response_id) as responses ' - .' FROM '.TICKET_MESSAGE_TABLE.' msg ' - .' LEFT JOIN '.TICKET_RESPONSE_TABLE. ' resp ON(resp.msg_id=msg.msg_id) ' - .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach - ON (msg.ticket_id=attach.ticket_id AND msg.msg_id=attach.ref_id AND ref_type="M") ' + $sql='SELECT msg.id, msg.created, msg.body ' + .' ,count(DISTINCT attach.attach_id) as attachments ' + .' ,count( DISTINCT resp.id) as responses ' + .' FROM '.TICKET_THREAD_TABLE.' msg ' + .' LEFT JOIN '.TICKET_THREAD_TABLE.' resp ON (' + .'resp.pid=msg.id AND resp.thread_type = "R") ' + .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ' + .'ON (msg.ticket_id=attach.ticket_id AND msg.id=attach.ref_id AND ref_type="M") ' .' WHERE msg.ticket_id='.db_input($this->getId()) - .' GROUP BY msg.msg_id ' + .' AND msg.thread_type="M"' + .' GROUP BY msg.id ' .' ORDER BY msg.created ASC '; $messages=array(); @@ -560,11 +575,12 @@ class Ticket{ function getResponses($msgId) { $sql='SELECT resp.*, count(DISTINCT attach.attach_id) as attachments ' - .' FROM '.TICKET_RESPONSE_TABLE. ' resp ' + .' FROM '.TICKET_THREAD_TABLE. ' resp ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach - ON (resp.ticket_id=attach.ticket_id AND resp.response_id=attach.ref_id AND ref_type="R") ' + ON (resp.ticket_id=attach.ticket_id AND resp.id=attach.ref_id AND ref_type="R") ' .' WHERE resp.ticket_id='.db_input($this->getId()) - .' GROUP BY resp.response_id ' + .' AND resp.thread_type="R"' + .' GROUP BY resp.id ' .' ORDER BY resp.created'; $responses=array(); @@ -633,7 +649,7 @@ class Ticket{ .', priority_id='.db_input($priorityId) .' WHERE ticket_id='.db_input($this->getId()); - return (db_query($sql) && db_affected_rows($res)); + return (($res=db_query($sql)) && db_affected_rows($res)); } //DeptId can NOT be 0. No orphans please! @@ -653,10 +669,14 @@ class Ticket{ //Set staff ID...assign/unassign/release (id can be 0) function setStaffId($staffId){ - $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), staff_id='.db_input($staffId) - .' WHERE ticket_id='.db_input($this->getId()); + $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), staff_id='.db_input($staffId) + .' WHERE ticket_id='.db_input($this->getId()); - return (db_query($sql) && db_affected_rows()); + if (db_query($sql) && db_affected_rows()) { + $this->staff_id = $staffId; + return true; + } + return false; } function setSLAId($slaId) { @@ -768,7 +788,7 @@ class Ticket{ $sql.=' WHERE ticket_id='.db_input($this->getId()); - $this->track('closed'); + $this->logEvent('closed'); return (db_query($sql) && db_affected_rows()); } @@ -782,7 +802,7 @@ class Ticket{ //TODO: log reopen event here - $this->track('reopened'); + $this->logEvent('reopened', 'closed'); return (db_query($sql) && db_affected_rows()); } @@ -872,7 +892,7 @@ class Ticket{ $msg=sprintf('Max open tickets (%d) reached for %s ', $cfg->getMaxOpenTickets(), $this->getEmail()); sys::log(LOG_WARNING, 'Max. Open Tickets Limit ('.$this->getEmail().')', $msg); - if(!$sendNotice || !$cfg->sendOverlimitNotice()) return true; + if(!$sendNotice || !$cfg->sendOverLimitNotice()) return true; //Send notice to user. $dept = $this->getDept(); @@ -952,14 +972,14 @@ class Ticket{ $email=$cfg->getDefaultEmail(); if($email) { - $email->send($this->getEMail(),$subj,$body); + $email->send($this->getEmail(),$subj,$body); } } } function onAssign($note, $alert=true) { - global $cfg; + global $cfg, $thisstaff; if($this->isClosed()) $this->reopen(); //Assigned tickets must be open - otherwise why assign? @@ -995,12 +1015,12 @@ class Ticket{ $recipients=array(); //Assigned staff or team... if any // Assigning a ticket to a team when already assigned to staff disables alerts to the team (!)) - if($cfg->alertStaffONAssign() && $this->getStaffId()) + if($cfg->alertStaffONAssignment() && $this->getStaffId()) $recipients[]=$this->getStaff(); elseif($this->getTeamId() && ($team=$this->getTeam())) { - if($cfg->alertTeamMembersOnAssignment() && ($members=$team->getMembers())) + if($cfg->alertTeamMembersONAssignment() && ($members=$team->getMembers())) $recipients+=$members; - elseif($cfg->alertTeamLeadOnAssignment() && ($lead=$team->getTeamLead())) + elseif($cfg->alertTeamLeadONAssignment() && ($lead=$team->getTeamLead())) $recipients[]=$lead; } //Send the alerts. @@ -1016,7 +1036,7 @@ class Ticket{ return true; } - function onOverdue($whine=true) { + function onOverdue($whine=true, $comments="") { global $cfg; if($whine && ($sla=$this->getSLA()) && !$sla->alertOnOverdue()) @@ -1026,8 +1046,9 @@ class Ticket{ if(!$whine || !$cfg->alertONOverdueTicket()) return true; - //Get template. - if(!($tpl = $dept->getTemplate())) + $dept = $this->getDept(); + //Get department-defined or default template. + if(!$dept || !($tpl = $dept->getTemplate())) $tpl= $cfg->getDefaultTemplate(); //Email to use! @@ -1126,8 +1147,8 @@ class Ticket{ if(!db_query($sql) || !db_affected_rows()) return false; + $this->logEvent('overdue'); $this->onOverdue($whine); - $this->track('overdue'); return true; } @@ -1148,6 +1169,7 @@ class Ticket{ $this->reopen(); $this->reload(); //reload - new dept!! + $this->logEvent('transferred'); //Send out alerts if enabled AND requested if(!$alert || !$cfg->alertONTransfer() || !($dept=$this->getDept())) return true; //no alerts!! @@ -1194,7 +1216,6 @@ class Ticket{ } } - $this->track('transferred'); return true; } @@ -1207,8 +1228,8 @@ class Ticket{ return false; $this->onAssign($note, $alert); + $this->logEvent('assigned'); - $this->track('assigned'); return true; } @@ -1226,8 +1247,8 @@ class Ticket{ $this->setStaffId(0); $this->onAssign($note, $alert); + $this->logEvent('assigned'); - $this->track('assigned'); return true; } @@ -1268,18 +1289,18 @@ class Ticket{ } //Insert message from client - function postMessage($msg,$source='',$msgid=NULL,$headers='',$newticket=false){ + function postMessage($msg,$source='',$emsgid=null,$headers='',$newticket=false){ global $cfg; if(!$this->getId()) return 0; # XXX: Refuse auto-response messages? (via email) XXX: No - but kill our auto-responder. - $sql='INSERT INTO '.TICKET_MESSAGE_TABLE.' SET created=NOW() ' + $sql='INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW()' + .' ,thread_type="M" ' .' ,ticket_id='.db_input($this->getId()) - .' ,messageId='.db_input($msgid) - .' ,message='.db_input(Format::striptags($msg)) //Tags/code stripped...meaning client can not send in code..etc - .' ,headers='.db_input($headers) //Raw header. + # XXX: Put Subject header into the 'title' field + .' ,body='.db_input(Format::striptags($msg)) //Tags/code stripped...meaning client can not send in code..etc .' ,source='.db_input($source?$source:$_SERVER['REMOTE_ADDR']) .' ,ip_address='.db_input($_SERVER['REMOTE_ADDR']); @@ -1287,6 +1308,15 @@ class Ticket{ $this->setLastMsgId($msgid); + if ($emsgid !== null) { + $sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE + .' SET msg_id='.db_input($msgid) + .', email_mid='.db_input($emsgid) + .', headers='.db_input($headers); + + if (!db_query($sql)) return 0; + } + if($newticket) return $msgid; //Our work is done... $autorespond = true; @@ -1351,12 +1381,13 @@ class Ticket{ if($errors) return 0; - $sql='INSERT INTO '.TICKET_RESPONSE_TABLE.' SET created=NOW() ' + $sql='INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW() ' + .' ,thread_type="R"' .' ,ticket_id='.db_input($this->getId()) - .' ,msg_id='.db_input($vars['msgId']) - .' ,response='.db_input(Format::striptags($vars['response'])) + .' ,pid='.db_input($vars['msgId']) + .' ,body='.db_input(Format::striptags($vars['response'])) .' ,staff_id='.db_input($thisstaff->getId()) - .' ,staff_name='.db_input($thisstaff->getName()) + .' ,poster='.db_input($thisstaff->getName()) .' ,ip_address='.db_input($thisstaff->getIP()); if(!db_query($sql) || !($respId=db_insert_id())) @@ -1384,16 +1415,17 @@ class Ticket{ $this->onResponse(); //do house cleaning.. $this->reload(); - $dept = $this->getDept(); /* email the user?? - if disabled - the bail out */ if(!$alert) return $respId; + $dept = $this->getDept(); + if(!($tpl = $dept->getTemplate())) $tpl= $cfg->getDefaultTemplate(); - if(!($email=$cfg->getAlertEmail())) - $email =$cfg->getDefaultEmail(); + if(!$dept || !($email=$dept->getEmail())) + $email = $cfg->getDefaultEmail(); if($tpl && ($msg=$tpl->getReplyMsgTemplate()) && $email) { $body=$this->replaceTemplateVars($msg['body']); @@ -1428,20 +1460,32 @@ class Ticket{ if(!$cfg || !$cfg->logTicketActivity()) return 0; - return $this->postNote($title,$note,false,'system'); + return $this->postNote($title,$note,false,'System'); } // History log -- used for statistics generation (pretty reports) - function track($state, $staff=null) { + function logEvent($state, $annul=null, $staff=null) { global $thisstaff; if ($staff === null) { if ($thisstaff) $staff=$thisstaff->getUserName(); else $staff='SYSTEM'; # XXX: Security Violation ? } + # Annul previous entries if requested (for instance, reopening a + # ticket will annul an 'closed' entry). This will be useful to + # easily prevent repeated statistics. + if ($annul) { + db_query('UPDATE '.TICKET_EVENT_TABLE.' SET annulled=1' + .' WHERE ticket_id='.db_input($this->getId()) + .' AND state='.db_input($annul)); + } - return db_query('INSERT INTO '.TICKET_HISTORY_TABLE + return db_query('INSERT INTO '.TICKET_EVENT_TABLE .' SET ticket_id='.db_input($this->getId()) + .', staff_id='.db_input($this->getStaffId()) + .', team_id='.db_input($this->getTeamId()) + .', dept_id='.db_input($this->getDeptId()) + .', topic_id='.db_input($this->getTopicId()) .', timestamp=NOW(), state='.db_input($state) .', staff='.db_input($staff)) && db_affected_rows() == 1; @@ -1451,12 +1495,13 @@ class Ticket{ function postNote($title,$note,$alert=true,$poster='') { global $thisstaff,$cfg; - $sql= 'INSERT INTO '.TICKET_NOTE_TABLE.' SET created=NOW() '. + $sql= 'INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW() '. + ',thread_type="N"'. ',ticket_id='.db_input($this->getId()). ',title='.db_input(Format::striptags($title)). - ',note='.db_input(Format::striptags($note)). + ',body='.db_input(Format::striptags($note)). ',staff_id='.db_input($thisstaff?$thisstaff->getId():0). - ',source='.db_input(($poster || !$thisstaff)?$poster:$thisstaff->getName()); + ',poster='.db_input(($poster || !$thisstaff)?$poster:$thisstaff->getName()); //echo $sql; if(!db_query($sql) || !($id=db_insert_id())) return false; @@ -1558,9 +1603,7 @@ class Ticket{ if(!db_query($sql) || !db_affected_rows()) return false; - db_query('DELETE FROM '.TICKET_MESSAGE_TABLE.' WHERE ticket_id='.db_input($this->getId())); - db_query('DELETE FROM '.TICKET_RESPONSE_TABLE.' WHERE ticket_id='.db_input($this->getId())); - db_query('DELETE FROM '.TICKET_NOTE_TABLE.' WHERE ticket_id='.db_input($this->getId())); + db_query('DELETE FROM '.TICKET_THREAD_TABLE.' WHERE ticket_id='.db_input($this->getId())); $this->deleteAttachments(); return true; @@ -1676,8 +1719,9 @@ class Ticket{ return 0; $sql='SELECT ticket.ticket_id FROM '.TICKET_TABLE. ' ticket '. - ' LEFT JOIN '.TICKET_MESSAGE_TABLE.' msg USING(ticket_id) '. - ' WHERE messageId='.db_input($mid).' AND email='.db_input($email); + ' LEFT JOIN '.TICKE_THREAD_TABLE.' msg USING(ticket_id) '. + ' INNER JOIN '.TICKET_EMAIL_INFO_TABLE.' emsg ON (msg.id = emsg.message_id) '. + ' WHERE email_mid='.db_input($mid).' AND email='.db_input($email); $id=0; if(($res=db_query($sql)) && db_num_rows($res)) list($id)=db_fetch_row($res); @@ -1776,7 +1820,7 @@ class Ticket{ if($cfg->getMaxOpenTickets()>0 && strcasecmp($origin,'staff') && ($client=Client::lookupByEmail($vars['email'])) && ($openTickets=$client->getNumOpenTickets()) - && ($opentickets>=$cfg->getMaxOpenTickets()) ) { + && ($openTickets>=$cfg->getMaxOpenTickets()) ) { $errors['err']="You've reached the maximum open tickets allowed."; Sys::log(LOG_WARNING, 'Ticket denied -'.$vars['email'], @@ -1960,6 +2004,9 @@ class Ticket{ && ($client->getNumOpenTickets()==$cfg->getMaxOpenTickets())) { $ticket->onOpenLimit(($autorespond && strcasecmp($origin, 'staff'))); } + + /* Start tracking ticket lifecycle events */ + $ticket->logEvent('created'); /* Phew! ... time for tea (KETEPA) */ @@ -1976,8 +2023,8 @@ class Ticket{ else $vars['message']=$vars['issue']; - if($var['source'] && !in_array(strtolower($var['source']),array('email','phone','other'))) - $errors['source']='Invalid source - '.Format::htmlchars($var['source']); + if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other'))) + $errors['source']='Invalid source - '.Format::htmlchars($vars['source']); if(!($ticket=Ticket::create($vars, $errors, 'staff', false, (!$vars['assignId'])))) return false; @@ -1995,7 +2042,7 @@ class Ticket{ } } //Post Internal note - if($var['assignId'] && $thisstaff->canAssignTickets()) { //Assign ticket to staff or team. + if($vars['assignId'] && $thisstaff->canAssignTickets()) { //Assign ticket to staff or team. $ticket->assign($vars['assignId'],$vars['note']); } elseif($vars['note']) { //Not assigned...save optional note if any $ticket->postNote('New Ticket',$vars['note'],false); diff --git a/include/class.validator.php b/include/class.validator.php index ea2dc62d0e079513940885b8f1734c4025bb178f..d41bf027fe826c855893146dfee680c578598feb 100644 --- a/include/class.validator.php +++ b/include/class.validator.php @@ -147,20 +147,21 @@ class Validator { $urlregex = "^(https?)\:\/\/"; // USER AND PASS (optional) - $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"; + $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"; # nolint // HOSTNAME OR IP - $urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // http://x = allowed (ex. http://localhost, http://routerlogin) + // http://x = allowed (ex. http://localhost, http://routerlogin) + $urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; # nolint //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // http://x.x = minimum //$urlregex .= "([a-z0-9+\$_-]+\.)*[a-z0-9+\$_-]{2,3}"; // http://x.xx(x) = minimum //use only one of the above // PORT (optional) $urlregex .= "(\:[0-9]{2,5})?"; // PATH (optional) - $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/?"; + $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/?"; # nolint // GET Query (optional) - $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?"; + $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?"; # nolint // ANCHOR (optional) - $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?\$"; + $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?\$"; # nolint return eregi($urlregex, $url)?true:false; } diff --git a/include/client/faq.inc.php b/include/client/faq.inc.php index c944f8316f58fb1b1ff59854bb7a63c05d505de5..65f770e9673083d2474d2a9eb260771492541d41 100644 --- a/include/client/faq.inc.php +++ b/include/client/faq.inc.php @@ -6,7 +6,7 @@ $category=$faq->getCategory(); ?> <h1>Frequently Asked Questions</h1> <div id="breadcrumbs"> - <a href="index.php">All Categories</a> + <a href="index.php">All Categories</a> » <a href="faq.php?cid=<? echo $category->getId(); ?>"><? echo $category->getName(); ?></a> </div> <div style="width:700;padding-top:2px; float:left;"> @@ -23,7 +23,8 @@ if($faq->getNumAttachments()) { ?> <div><span class="faded"><b>Attachments:</b></span> <?php echo $faq->getAttachmentsLinks(); ?></div> <? }?> -<div><span class="faded"><b>Help Topics:</b></span> + +<div class="article-meta"><span class="faded"><b>Help Topics:</b></span> <?php echo ($topics=$faq->getHelpTopics())?implode(', ',$topics):' '; ?> </div> </p> diff --git a/include/client/header.inc.php b/include/client/header.inc.php index beefb3bf3c74581da257a6ef684ed5553d5d96c1..969e9e66b8d977256e8087fc8cba2dadebb8fc62 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -9,7 +9,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <title><?php echo Format::htmlchars($title); ?></title> <meta name="description" content="customer support platform"> <meta name="keywords" content="osTicket, Customer support system, support ticket system"> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/theme.css" media="screen"> <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/print.css" media="print"> <script src="./js/jquery-1.7.2.min.js"></script> @@ -21,19 +21,19 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <a id="logo" href="<?php echo ROOT_PATH; ?>index.php" title="Support Center"><img src="<?php echo ASSETS_PATH; ?>images/logo.png" border=0 alt="Support Center"></a> <p> <?php - if($thisclient && is_object($thisclient) && $thisclient->isValid()) { + if($thisclient && is_object($thisclient) && $thisclient->isValid()) { echo $thisclient->getName().' - '; ?> <?php if($cfg->showRelatedTickets()) {?> - <a href="<?php echo ROOT_PATH; ?>tickets.php">My Tickets <b>(<?php echo $thisclient->getNumTickets(); ?>)</b></a> - + <a href="<?php echo ROOT_PATH; ?>tickets.php">My Tickets <b>(<?php echo $thisclient->getNumTickets(); ?>)</b></a> - <?php } ?> <a href="<?php echo ROOT_PATH; ?>logout.php">Log Out</a> - <?php + <?php }elseif($nav){ ?> Guest User - <a href="<?php echo ROOT_PATH; ?>login.php">Log In</a> - <?php + <?php } ?> </p> </div> @@ -47,14 +47,13 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); } } ?> </ul> - <div id="content"> <?php }else{ ?> <hr> - <div id="cnbg"> <?php } ?> - + <div id="content"> + <?php if($errors['err']) { ?> <div id="msg_error"><?php echo $errors['err']; ?></div> <?php }elseif($msg) { ?> diff --git a/include/client/knowledgebase.inc.php b/include/client/knowledgebase.inc.php index 83484e95de818d732a67fed39b980fd5e9262e95..d8f402e7b5d191b224dc638b54f3a3177bddce85 100644 --- a/include/client/knowledgebase.inc.php +++ b/include/client/knowledgebase.inc.php @@ -3,61 +3,53 @@ if(!defined('OSTCLIENTINC')) die('Access Denied'); ?> <h1>Frequently Asked Questions</h1> -<form action="index.php" method="get" style="padding-top:15px;"> +<form action="index.php" method="get" id="kb-search"> <input type="hidden" name="a" value="search"> - <table border="0" cellspacing="0" cellpadding="3"> - <tr> - <td width="440"> - <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> - <select name="cid"> - <option value="">— All Categories —</option> - <?php - $sql='SELECT category_id, name, count(faq.category_id) as faqs ' - .' FROM '.FAQ_CATEGORY_TABLE.' cat ' - .' LEFT JOIN '.FAQ_TABLE.' faq USING(category_id) ' - .' WHERE cat.ispublic=1 AND faq.ispublished=1 ' - .' GROUP BY cat.category_id ' - .' HAVING faqs>0 ' - .' ORDER BY cat.name DESC '; - if(($res=db_query($sql)) && db_num_rows($res)) { - while($row=db_fetch_array($res)) - echo sprintf('<option value="%d" %s>%s (%d)</option>', - $row['category_id'], - ($_REQUEST['cid'] && $row['category_id']==$_REQUEST['cid']?'selected="selected"':''), - $row['name'], - $row['faqs']); - } - ?> - </select> - </td> - <td width="100" rowspan="2" style="text-align:left;vertical-align: middle;"> - <input id="searchSubmit" type="submit" value="Search"> - </td> - </tr> - <tr> - <td width="400"> - <select name="topicId" style="width:350px;"> - <option value="">— All Help Topics —</option> - <?php - $sql='SELECT ht.topic_id, ht.topic, count(faq.topic_id) as faqs ' - .' FROM '.TOPIC_TABLE.' ht ' - .' LEFT JOIN '.FAQ_TOPIC_TABLE.' faq USING(topic_id) ' - .' WHERE ht.ispublic=1 ' - .' GROUP BY ht.topic_id ' - .' HAVING faqs>0 ' - .' ORDER BY ht.topic DESC '; - if(($res=db_query($sql)) && db_num_rows($res)) { - while($row=db_fetch_array($res)) - echo sprintf('<option value="%d" %s>%s (%d)</option>', - $row['topic_id'], - ($_REQUEST['topicId'] && $row['topic_id']==$_REQUEST['topicId']?'selected="selected"':''), - $row['topic'], $row['faqs']); - } - ?> - </select> - </td> - </tr> - </table> + <div> + <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> + <select name="cid" id="cid"> + <option value="">— All Categories —</option> + <?php + $sql='SELECT category_id, name, count(faq.category_id) as faqs ' + .' FROM '.FAQ_CATEGORY_TABLE.' cat ' + .' LEFT JOIN '.FAQ_TABLE.' faq USING(category_id) ' + .' WHERE cat.ispublic=1 AND faq.ispublished=1 ' + .' GROUP BY cat.category_id ' + .' HAVING faqs>0 ' + .' ORDER BY cat.name DESC '; + if(($res=db_query($sql)) && db_num_rows($res)) { + while($row=db_fetch_array($res)) + echo sprintf('<option value="%d" %s>%s (%d)</option>', + $row['category_id'], + ($_REQUEST['cid'] && $row['category_id']==$_REQUEST['cid']?'selected="selected"':''), + $row['name'], + $row['faqs']); + } + ?> + </select> + <input id="searchSubmit" type="submit" value="Search"> + </div> + <div> + <select name="topicId" id="topic-id"> + <option value="">— All Help Topics —</option> + <?php + $sql='SELECT ht.topic_id, ht.topic, count(faq.topic_id) as faqs ' + .' FROM '.TOPIC_TABLE.' ht ' + .' LEFT JOIN '.FAQ_TOPIC_TABLE.' faq USING(topic_id) ' + .' WHERE ht.ispublic=1 ' + .' GROUP BY ht.topic_id ' + .' HAVING faqs>0 ' + .' ORDER BY ht.topic DESC '; + if(($res=db_query($sql)) && db_num_rows($res)) { + while($row=db_fetch_array($res)) + echo sprintf('<option value="%d" %s>%s (%d)</option>', + $row['topic_id'], + ($_REQUEST['topicId'] && $row['topic_id']==$_REQUEST['topicId']?'selected="selected"':''), + $row['topic'], $row['faqs']); + } + ?> + </select> + </div> </form> <hr> <div> @@ -100,9 +92,10 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search. echo '<div>Click on the category to browse FAQs.</div> <ul id="kb">'; while($row=db_fetch_array($res)) { - + echo sprintf(' <li> + <i></i> <h4><a href="faq.php?cid=%d">%s (%d)</a></h4> %s </li>',$row['category_id'], diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php index a360217167bde83dedcb4decb339677e3c5370d6..7719ec74a7ef2a58babcae7112a0d2cb05caa92e 100644 --- a/include/client/tickets.inc.php +++ b/include/client/tickets.inc.php @@ -59,13 +59,12 @@ if($search) { $queryterm=db_real_escape($_REQUEST['q'],false); //escape the term ONLY...no quotes. $qwhere.=' AND ( ' ." ticket.subject LIKE '%$queryterm%'" - ." OR message.message LIKE '%$queryterm%'" - ." OR response.response LIKE '%$queryterm%'" + ." OR thread.body LIKE '%$queryterm%'" .' ) '; $deep_search=true; //Joins needed for search - $qfrom.=' LEFT JOIN '.TICKET_MESSAGE_TABLE.' message ON (ticket.ticket_id=message.ticket_id )' - .' LEFT JOIN '.TICKET_RESPONSE_TABLE.' response ON (ticket.ticket_id=response.ticket_id )'; + $qfrom.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (' + .'ticket.ticket_id=thread.ticket_id AND thread.thread_type IN ("M","R"))'; } } diff --git a/include/client/view.inc.php b/include/client/view.inc.php index e720ed96bffa58c5d7321e7fe2544c7829a86678..f67e6d94bd3a4456e30b17797254dff49204b960 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -67,11 +67,11 @@ if($ticket->getThreadCount() && ($messages = $ticket->getMessages())) { <tr><th><?php echo Format::db_datetime($message['created']); ?></th></tr> - <tr><td><?php echo Format::display($message['message']); ?></td></tr> + <tr><td><?php echo Format::display($message['body']); ?></td></tr> <?php - if($message['attachments'] && ($links=$ticket->getAttachmentsLinks($message['msg_id'],'M'))) { ?> + if($message['attachments'] && ($links=$ticket->getAttachmentsLinks($message['id'],'M'))) { ?> <tr><td class="info"><?php echo $links; ?></td></tr> @@ -81,7 +81,7 @@ if($ticket->getThreadCount() && ($messages = $ticket->getMessages())) { </table> <?php - if($message['responses'] && ($responses=$ticket->getResponses($message['msg_id']))) { + if($message['responses'] && ($responses=$ticket->getResponses($message['id']))) { foreach($responses as $resp) { $staff=$cfg->hideStaffName()?'staff':Format::htmlchars($resp['staff_name']); ?> @@ -89,9 +89,9 @@ if($ticket->getThreadCount() && ($messages = $ticket->getMessages())) { <tr> <th><?php echo Format::db_datetime($resp['created']);?> - <?php echo $staff; ?></th> </tr> - <tr><td><?php echo Format::display($resp['response']); ?></td></tr> + <tr><td><?php echo Format::display($resp['body']); ?></td></tr> <?php - if($resp['attachments'] && ($links=$ticket->getAttachmentsLinks($resp['response_id'],'R'))) {?> + if($resp['attachments'] && ($links=$ticket->getAttachmentsLinks($resp['id'],'R'))) {?> <tr><td class="info"><?php echo $links; ?></td></tr> <?php }?> diff --git a/include/mysql.php b/include/mysql.php index 192859af970722c9127e1c57c7596085e98290f5..47f5ab5c8854e7ce4f6cdea47cd5ebd7dccd2fc4 100644 --- a/include/mysql.php +++ b/include/mysql.php @@ -46,8 +46,10 @@ function db_version(){ $version=0; - if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/', mysql_result(db_query('SELECT VERSION()'),0,0),$matches)) - $version=$matches[1]; + if(preg_match('/(\d{1,2}\.\d{1,2}\.\d{1,2})/', + mysql_result(db_query('SELECT VERSION()'),0,0), + $matches)) # nolint + $version=$matches[1]; # nolint return $version; } diff --git a/include/pear/Mail/mimeDecode.php b/include/pear/Mail/mimeDecode.php index b7984d681e38a30e5e546d2846019b93b38f3c33..59b6e1923c32d5ea5a3816e5c14821b1913fd3bf 100644 --- a/include/pear/Mail/mimeDecode.php +++ b/include/pear/Mail/mimeDecode.php @@ -721,6 +721,7 @@ class Mail_mimeDecode extends PEAR $this->_decode_headers = FALSE; $headerlist =$this->_parseHeaders($this->_header); $to = ""; + $header = array(); if (!$headerlist) { return $this->raiseError("Message did not contain headers"); } diff --git a/include/staff/api.inc.php b/include/staff/api.inc.php deleted file mode 100644 index 21eac469162af3d2cc9652d8e8255f230ee18460..0000000000000000000000000000000000000000 --- a/include/staff/api.inc.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); - - -$info['phrase']=($errors && $_POST['phrase'])?Format::htmlchars($_POST['phrase']):$cfg->getAPIPassphrase(); -$select='SELECT * '; -$from='FROM '.API_KEY_TABLE; -$where=''; -$sortOptions=array('date'=>'created','ip'=>'ipaddr'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -//Sorting options... -if($_REQUEST['sort']) { - $order_column =$sortOptions[$_REQUEST['sort']]; -} - -if($_REQUEST['order']) { - $order=$orderWays[$_REQUEST['order']]; -} -$order_column=$order_column?$order_column:'ipaddr'; -$order=$order?$order:'ASC'; -$order_by=" ORDER BY $order_column $order "; - -$total=db_count('SELECT count(*) '.$from.' '.$where); -$pagelimit=1000;//No limit. TODO: Add limit. -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($total,$page,$pagelimit); -$pageNav->setURL('admin.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order'])); -$query="$select $from $where $order_by"; -//echo $query; -$result = db_query($query); -$showing=db_num_rows($result)?$pageNav->showing():''; -$negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. -$deletable=0; -?> -<div class="msg">API Keys</div> -<hr> -<div><b><?php echo $showing; ?></b></div> - <table width="100%" border="0" cellspacing=1 cellpadding=2> - <form action="admin.php?t=api" method="POST" name="api" onSubmit="return checkbox_checker(document.forms['api'],1,0);"> - <input type=hidden name='t' value='api'> - <input type=hidden name='do' value='mass_process'> - <tr><td> - <table border="0" cellspacing=0 cellpadding=2 class="dtable" align="center" width="100%"> - <tr> - <th width="7px"> </th> - <th>API Key</th> - <th width="10" nowrap>Active</th> - <th width="100" nowrap> IP Address</th> - <th width="150" nowrap> - <a href="admin.php?t=api&sort=date&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Create Date <?php echo $negorder; ?>">Created</a></th> - </tr> - <?php - $class = 'row1'; - $total=0; - $active=$inactive=0; - $sids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($result && db_num_rows($result)): - $dtpl=$cfg->getDefaultTemplateId(); - while ($row = db_fetch_array($result)) { - $sel=false; - $disabled=''; - if($row['isactive']) - $active++; - else - $inactive++; - - if($sids && in_array($row['id'],$sids)){ - $class="$class highlight"; - $sel=true; - } - ?> - <tr class="<?php echo $class; ?>" id="<?php echo $row['id']; ?>"> - <td width=7px> - <input type="checkbox" name="ids[]" value="<?php echo $row['id']; ?>" <?php echo $sel?'checked':''; ?> - onClick="highLight(this.value,this.checked);"> - <td> <?php echo $row['apikey']; ?></td> - <td><?php echo $row['isactive']?'<b>Yes</b>':'No'; ?></td> - <td> <?php echo $row['ipaddr']; ?></td> - <td> <?php echo Format::db_datetime($row['created']); ?></td> - </tr> - <?php - $class = ($class =='row2') ?'row1':'row2'; - } //end of while. - else: //nothin' found!! ?> - <tr class="<?php echo $class; ?>"><td colspan=5><b>Query returned 0 results</b> <a href="admin.php?t=templates">Index list</a></td></tr> - <?php - endif; ?> - - </table> - </td></tr> - <?php - if(db_num_rows($result)>0): //Show options.. - ?> - <tr> - <td align="center"> - <?php - if($inactive) { ?> - <input class="button" type="submit" name="enable" value="Enable" - onClick='return confirm("Are you sure you want to ENABLE selected keys?");'> - <?php - } - if($active){ ?> - - <input class="button" type="submit" name="disable" value="Disable" - onClick='return confirm("Are you sure you want to DISABLE selected keys?");'> - <?php } ?> - - <input class="button" type="submit" name="delete" value="Delete" - onClick='return confirm("Are you sure you want to DELETE selected keys?");'> - </td> - </tr> - <?php - endif; - ?> - </form> - </table> - <br/> - <div class="msg">Add New IP</div> - <hr> - <div> - Add a new IP address. <font class="error"><?php echo $errors['ip']; ?></font> - <form action="admin.php?t=api" method="POST" > - <input type=hidden name='t' value='api'> - <input type=hidden name='do' value='add'> - New IP: - <input name="ip" size=30 value="<?php echo ($errors['ip'])?Format::htmlchars($_REQUEST['ip']):''; ?>" /> - <font class="error">* </font> - <input class="button" type="submit" name="add" value="Add"> - </form> - </div> - <br/> - <div class="msg">API Passphrase</div> - <hr> - <div> - Passphrase must be at least 3 words. Required to generate the api keys.<br/> - <form action="admin.php?t=api" method="POST" > - <input type=hidden name='t' value='api'> - <input type=hidden name='do' value='update_phrase'> - Phrase: - <input name="phrase" size=50 value="<?php echo Format::htmlchars($info['phrase']); ?>" /> - <font class="error">* <?php echo $errors['phrase']; ?></font> - <input class="button" type="submit" name="update" value="Submit"> - </form> - <br/><br/> - <div><i>Please note that changing the passprase does NOT invalidate existing keys. To regerate a key you need to delete and readd it.</i></div> - </div> - diff --git a/include/staff/apikey.inc.php b/include/staff/apikey.inc.php index 5a82ee39bf264a821de957a779c0a73087f78bd9..7bcac1cb03e257feb855fbf0436769b8b3a3650e 100644 --- a/include/staff/apikey.inc.php +++ b/include/staff/apikey.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($api && $_REQUEST['a']!='add'){ diff --git a/include/staff/apikeys.inc.php b/include/staff/apikeys.inc.php index d7063c749ce92c04cddc36f20902b88181ab51d4..3deccb941222f8d771eb120ea90ab7e27557203c 100644 --- a/include/staff/apikeys.inc.php +++ b/include/staff/apikeys.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT * FROM '.API_KEY_TABLE.' WHERE 1'; diff --git a/include/staff/attachment.inc.php b/include/staff/attachment.inc.php index 17819867e69a458517e7c312075c07ffe3eeb06c..5951b87c0b0788767b34c98d1a7290a7c1debcdd 100644 --- a/include/staff/attachment.inc.php +++ b/include/staff/attachment.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); //Get the config info. $config=($errors && $_POST)?Format::input($_POST):$cfg->getConfig(); ?> diff --git a/include/staff/banlist.inc.php b/include/staff/banlist.inc.php index ecef00ba4325e18339b4f733d82a34b38466b950..430b51f4835bbf7038941109621774b409485b2e 100644 --- a/include/staff/banlist.inc.php +++ b/include/staff/banlist.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin() || !$filter) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$filter) die('Access Denied'); $qstr=''; $select='SELECT rule.* '; diff --git a/include/staff/banrule.inc.php b/include/staff/banrule.inc.php index bd28d19dea8368b395122beeb861045205d7ddff..0560b4a4d6268961220dad69de5d7b73617afec2 100644 --- a/include/staff/banrule.inc.php +++ b/include/staff/banrule.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; diff --git a/include/staff/cannedreplies.inc.php b/include/staff/cannedreplies.inc.php index 601e27d37f49646b4043b57a717abb64cb626b96..39aaaae815a8479233e3959c26b4dc28764b1ef0 100644 --- a/include/staff/cannedreplies.inc.php +++ b/include/staff/cannedreplies.inc.php @@ -50,7 +50,7 @@ else <h2>Canned Replies</h2> </div> <div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"> - <b><a href="canned.php?a=add" class="Icon newHelpTopic">Add New Reply</a></b></div> + <b><a href="canned.php?a=add" class="Icon newReply">Add New Reply</a></b></div> <div class="clear"></div> <form action="canned.php" method="POST" name="canned" onSubmit="return checkbox_checker(this,1,0);"> <input type="hidden" name="do" value="mass_process" > @@ -58,7 +58,7 @@ else <caption><?php echo $showing; ?></caption> <thead> <tr> - <th width="7"> </th> + <th width="7"> </th> <th width="500"><a <?php echo $title_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=title">Title</a></th> <th width="80"><a <?php echo $status_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=status">Status</a></th> <th width="200"><a <?php echo $dept_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=dept">Department</a></th> @@ -81,7 +81,7 @@ else ?> <tr id="<?php echo $row['canned_id']; ?>"> <td width=7px> - <input type="checkbox" name="ids[]" value="<?php echo $row['canned_id']; ?>" + <input type="checkbox" name="ids[]" value="<?php echo $row['canned_id']; ?>" <?php echo $sel?'checked="checked"':''; ?> <?php echo $default?'disabled="disabled"':''; ?> onClick="highLight(this.value,this.checked);"> </td> <td> diff --git a/include/staff/categories.inc.php b/include/staff/categories.inc.php index a437338c20a156550487faf891324e2a16406562..9e90869c0d24acd79eabced4a1eed76c133681cc 100644 --- a/include/staff/categories.inc.php +++ b/include/staff/categories.inc.php @@ -44,7 +44,7 @@ else <h2>FAQ Categories</h2> </div> <div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"> - <b><a href="categories.php?a=add" class="Icon newHelpTopic">Add New Category</a></b></div> + <b><a href="categories.php?a=add" class="Icon newCategory">Add New Category</a></b></div> <div class="clear"></div> <form action="categories.php" method="POST" name="cat" onSubmit="return checkbox_checker(this,1,0);"> <input type="hidden" name="do" value="mass_process" > @@ -52,7 +52,7 @@ else <caption><?php echo $showing; ?></caption> <thead> <tr> - <th width="7"> </th> + <th width="7"> </th> <th width="500"><a <?php echo $name_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=name">Name</a></th> <th width="150"><a <?php echo $type_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=type">Type</a></th> <th width="80"><a <?php echo $faqs_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=faqs">FAQs</a></th> @@ -78,7 +78,7 @@ else ?> <tr id="<?php echo $row['category_id']; ?>"> <td width=7px> - <input type="checkbox" name="ids[]" value="<?php echo $row['category_id']; ?>" + <input type="checkbox" name="ids[]" value="<?php echo $row['category_id']; ?>" <?php echo $sel?'checked="checked"':''; ?> <?php echo $default?'disabled="disabled"':''; ?> onClick="highLight(this.value,this.checked);"> </td> <td><a href="categories.php?id=<?php echo $row['category_id']; ?>"><?php echo Format::truncate($row['name'],200); ?></a> </td> diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php index fa32ec2795d1ab1635fa126ab639a5d1259f3258..521cee9bf9e9eb7446420c26c4e0ab03a1c3ad69 100644 --- a/include/staff/department.inc.php +++ b/include/staff/department.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($dept && $_REQUEST['a']!='add'){ diff --git a/include/staff/departments.inc.php b/include/staff/departments.inc.php index 805b7ec20706c7cbbfa1bb2dd331068aa3bc8154..71b702a0676cff5f6824c933fc458882fff9e991 100644 --- a/include/staff/departments.inc.php +++ b/include/staff/departments.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT dept.dept_id,dept_name,email.email_id,email.email,email.name as email_name,ispublic,count(staff.staff_id) as users '. diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php index 4f2d695b09304c4dcd5fc850e32ede734c514bbf..2fd2b8857494c64c061524a5bd294e4433299ee4 100644 --- a/include/staff/email.inc.php +++ b/include/staff/email.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($email && $_REQUEST['a']!='add'){ diff --git a/include/staff/emails.inc.php b/include/staff/emails.inc.php index 65d34d7b9bfb0b10acd983bfe41e5671932d3060..8d5f221177051e484fcaff4457c5f9f60bffbc99 100644 --- a/include/staff/emails.inc.php +++ b/include/staff/emails.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT email.*,dept.dept_name as department,priority_desc as priority '. diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php index 91f74501661cda00d97aada035b28559a9961d41..6f8cce2c41a1e6acd8f4810b3cdc7e643a9a4471 100644 --- a/include/staff/filter.inc.php +++ b/include/staff/filter.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $matches=array('name'=>"Sender's Name",'email'=>"Sender's Email",'subject'=>'Email Subject','body'=>'Email Body/Text','header'=>'Email Header'); $match_types=array('equal'=>'Equal','not_equal'=>'Not Equal','contains'=>'Contains','dn_contain'=>'Does Not Contain'); diff --git a/include/staff/filters.inc.php b/include/staff/filters.inc.php index fb8a48d268a7705d5eb3a49b4076a6dcdb3b0b17..b5534bdde77a12ffe88a8f2362046c5d2e64fce2 100644 --- a/include/staff/filters.inc.php +++ b/include/staff/filters.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT filter.*,count(rule.id) as rules '. diff --git a/include/staff/group.inc.php b/include/staff/group.inc.php index 87921a63cb18aad7a2c73ab73ab5a2c7fb3d8acd..afb574f5f3cea7ff161dc34e90c05fa77f955b51 100644 --- a/include/staff/group.inc.php +++ b/include/staff/group.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($group && $_REQUEST['a']!='add'){ diff --git a/include/staff/groups.inc.php b/include/staff/groups.inc.php index 15b9b6610916c203cdf25e902228dd8d5281ba5a..ed5e3638a8d0528908584ddfad5320d885a38e4d 100644 --- a/include/staff/groups.inc.php +++ b/include/staff/groups.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; diff --git a/include/staff/header.inc.php b/include/staff/header.inc.php index 58d4f28dcdf77cbe3fa184e8a4979982b01de0fe..b0a7c4efef46bfa5ce923ac251beb101c3125293 100644 --- a/include/staff/header.inc.php +++ b/include/staff/header.inc.php @@ -26,7 +26,7 @@ <div id="container"> <div id="header"> <a href="index.php" id="logo">osTicket - Customer Support System</a> - <p id="info">Welcome back, <strong><?php echo $thisstaff->getUsername(); ?></strong> + <p id="info">Welcome back, <strong><?php echo $thisstaff->getUserName(); ?></strong> <?php if($thisstaff->isAdmin() && !defined('ADMINPAGE')) { ?> | <a href="admin.php">Admin Panel</a> diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index ad9f808bcfd6afedfabec510dda920ca2abaae0a..c0fdcd34010266a802bbffdd3f31c741c36f0b9a 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($topic && $_REQUEST['a']!='add'){ diff --git a/include/staff/helptopics.inc.php b/include/staff/helptopics.inc.php index 27ffde9bdacac59c2687b30b5046dde4255b1537..b3d58c777517753e02b41d1aaa703ed6e5500c01 100644 --- a/include/staff/helptopics.inc.php +++ b/include/staff/helptopics.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT topic.*,dept.dept_name as department,priority_desc as priority '. diff --git a/include/staff/kb-categories.inc.php b/include/staff/kb-categories.inc.php index 0ab8b03fc1e6326eac435768954c049dc0b3aa64..eb673a735678e3ebd867f3019b34443822a4b178 100644 --- a/include/staff/kb-categories.inc.php +++ b/include/staff/kb-categories.inc.php @@ -3,59 +3,51 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied'); ?> <h2>Frequently Asked Questions</h2> -<form id="kbSearch" action="kb.php" method="get" style="padding-top:15px;"> +<form id="kbSearch" action="kb.php" method="get"> <input type="hidden" name="a" value="search"> - <table border="0" cellspacing="0" cellpadding="3"> - <tr> - <td width="440"> - <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> - <select name="cid"> - <option value="">— All Categories —</option> - <?php - $sql='SELECT category_id, name, count(faq.category_id) as faqs ' - .' FROM '.FAQ_CATEGORY_TABLE.' cat ' - .' LEFT JOIN '.FAQ_TABLE.' faq USING(category_id) ' - .' GROUP BY cat.category_id ' - .' HAVING faqs>0 ' - .' ORDER BY cat.name DESC '; - if(($res=db_query($sql)) && db_num_rows($res)) { - while($row=db_fetch_array($res)) - echo sprintf('<option value="%d" %s>%s (%d)</option>', - $row['category_id'], - ($_REQUEST['cid'] && $row['category_id']==$_REQUEST['cid']?'selected="selected"':''), - $row['name'], - $row['faqs']); - } - ?> - </select> - </td> - <td width="100" rowspan="2"> - <input id="searchSubmit" type="submit" value="Search"> - </td> - </tr> - <tr> - <td width="400"> - <select name="topicId" style="width:350px;"> - <option value="">— All Help Topics —</option> - <?php - $sql='SELECT ht.topic_id, ht.topic, count(faq.topic_id) as faqs ' - .' FROM '.TOPIC_TABLE.' ht ' - .' LEFT JOIN '.FAQ_TOPIC_TABLE.' faq USING(topic_id) ' - .' GROUP BY ht.topic_id ' - .' HAVING faqs>0 ' - .' ORDER BY ht.topic DESC '; - if(($res=db_query($sql)) && db_num_rows($res)) { - while($row=db_fetch_array($res)) - echo sprintf('<option value="%d" %s>%s (%d)</option>', - $row['topic_id'], - ($_REQUEST['topicId'] && $row['topic_id']==$_REQUEST['cid']?'selected="selected"':''), - $row['topic'], $row['faqs']); - } - ?> - </select> - </td> - </tr> - </table> + <div> + <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> + <select name="cid" id="cid"> + <option value="">— All Categories —</option> + <?php + $sql='SELECT category_id, name, count(faq.category_id) as faqs ' + .' FROM '.FAQ_CATEGORY_TABLE.' cat ' + .' LEFT JOIN '.FAQ_TABLE.' faq USING(category_id) ' + .' GROUP BY cat.category_id ' + .' HAVING faqs>0 ' + .' ORDER BY cat.name DESC '; + if(($res=db_query($sql)) && db_num_rows($res)) { + while($row=db_fetch_array($res)) + echo sprintf('<option value="%d" %s>%s (%d)</option>', + $row['category_id'], + ($_REQUEST['cid'] && $row['category_id']==$_REQUEST['cid']?'selected="selected"':''), + $row['name'], + $row['faqs']); + } + ?> + </select> + <input id="searchSubmit" type="submit" value="Search"> + </div> + <div> + <select name="topicId" style="width:350px;" id="topic-id"> + <option value="">— All Help Topics —</option> + <?php + $sql='SELECT ht.topic_id, ht.topic, count(faq.topic_id) as faqs ' + .' FROM '.TOPIC_TABLE.' ht ' + .' LEFT JOIN '.FAQ_TOPIC_TABLE.' faq USING(topic_id) ' + .' GROUP BY ht.topic_id ' + .' HAVING faqs>0 ' + .' ORDER BY ht.topic DESC '; + if(($res=db_query($sql)) && db_num_rows($res)) { + while($row=db_fetch_array($res)) + echo sprintf('<option value="%d" %s>%s (%d)</option>', + $row['topic_id'], + ($_REQUEST['topicId'] && $row['topic_id']==$_REQUEST['cid']?'selected="selected"':''), + $row['topic'], $row['faqs']); + } + ?> + </select> + </div> </form> <hr> <div> @@ -96,7 +88,7 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search. echo '<div>Click on the category to browse FAQs.</div> <ul id="kb">'; while($row=db_fetch_array($res)) { - + echo sprintf(' <li> <h4><a href="kb.php?cid=%d">%s (%d)</a> - <span>%s</span></h4> diff --git a/include/staff/kb-category.inc.php b/include/staff/kb-category.inc.php index 65c428a2f703f0b58332bbd416426c50cec765e7..e7013192e995738c2f9be4f249c3292acac9ab53 100644 --- a/include/staff/kb-category.inc.php +++ b/include/staff/kb-category.inc.php @@ -11,23 +11,23 @@ if(!defined('OSTSTAFFINC') || !$category || !$thisstaff) die('Access Denied'); <div> <strong><?php echo $category->getName() ?></strong> <span>(<?php echo $category->isPublic()?'Public':'Internal'; ?>)</span> - <br> - <div class="faded"> Last updated <?php echo Format::db_daydatetime($category->getUpdateDate()); ?></div> + <time>Last updated <?php echo Format::db_daydatetime($category->getUpdateDate()); ?></time> </div> -<p> +<div class="cat-desc"> <?php echo Format::safe_html($category->getDescription()); ?> -</p> +</div> <?php if($thisstaff->canManageFAQ()) { - echo sprintf('<a href="categories.php?id=%d" class="Icon newHelpTopic">Edit Category</a> - | <a href="categories.php" class="Icon newHelpTopic">Delete Category</a> - | <a href="faq.php?cid=%d&a=add" class="Icon newHelpTopic">Add New FAQ</a>', + echo sprintf('<div class="cat-manage-bar"><a href="categories.php?id=%d" class="Icon editCategory">Edit Category</a> + <a href="categories.php" class="Icon deleteCategory">Delete Category</a> + <a href="faq.php?cid=%d&a=add" class="Icon newFAQ">Add New FAQ</a></div>', $category->getId(), $category->getId()); -} +} else { ?> <hr> <?php +} $sql='SELECT faq.faq_id, question, ispublished, count(attach.file_id) as attachments ' .' FROM '.FAQ_TABLE.' faq ' @@ -39,7 +39,7 @@ if(($res=db_query($sql)) && db_num_rows($res)) { <ol>'; while($row=db_fetch_array($res)) { echo sprintf(' - <li><a href="faq.php?id=%d" class="previewfaq">%s</a> - <span>%s</span></li>', + <li><a href="faq.php?id=%d" class="previewfaq">%s <span>- %s</span></a></li>', $row['faq_id'],$row['question'],$row['ispublished']?'Published':'Internal'); } echo ' </ol> diff --git a/include/staff/preference.inc.php b/include/staff/preference.inc.php index d956d011dff2c15ad87c7e023f69303a87034353..054d592a7555f8d16f436f9cc5c9c66a91b6f27b 100644 --- a/include/staff/preference.inc.php +++ b/include/staff/preference.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); //Get the config info. $config=($errors && $_POST)?Format::input($_POST):Format::htmlchars($cfg->getConfig()); diff --git a/include/staff/slaplan.inc.php b/include/staff/slaplan.inc.php index 91d896d639d688813389ed0582ecd800beb2a842..70db620e84d83dc66a53cf6c08e1d97ea2b31f24 100644 --- a/include/staff/slaplan.inc.php +++ b/include/staff/slaplan.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($sla && $_REQUEST['a']!='add'){ diff --git a/include/staff/slaplans.inc.php b/include/staff/slaplans.inc.php index 5dea61f69b8ccaeaec3b84411185fbe4a26da886..b8997b6a34be75b697624384e0e39006c5c16feb 100644 --- a/include/staff/slaplans.inc.php +++ b/include/staff/slaplans.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT * FROM '.SLA_TABLE.' sla WHERE 1'; diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php index 8883397f51fe86c6b46c5207a099ad33ec50ec31..39651227105158adcadf5283517589ff8dc52ce8 100644 --- a/include/staff/staff.inc.php +++ b/include/staff/staff.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; @@ -210,7 +210,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> <tr> <td width="180" class="required"> - User Default Time Zone: + Staff's Time Zone: </td> <td> <select name="timezone_id" id="timezone_id"> @@ -228,6 +228,16 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <span class="error">* <?php echo $errors['timezone_id']; ?></span> </td> </tr> + <tr> + <td width="180"> + Daylight Saving: + </td> + <td> + <input type="checkbox" name="daylight_saving" value="1" <?php echo $info['daylight_saving']?'checked="checked"':''; ?>> + Observe daylight saving + <em>(Current Time: <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['daylight_saving']); ?></strong>)</em> + </td> + </tr> <tr> <td width="180"> Limited Access: diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php index aa41b1bbf4a00a6143bb8bd0961d8a8ab4a801b0..b01e3387ff28da3e2f8e419b4e728293fe851254 100644 --- a/include/staff/staffmembers.inc.php +++ b/include/staff/staffmembers.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $select='SELECT staff.*,CONCAT_WS(" ",firstname,lastname) as name, grp.group_name, dept.dept_name as dept,count(m.team_id) as teams '; $from='FROM '.STAFF_TABLE.' staff '. diff --git a/include/staff/syslogs.inc.php b/include/staff/syslogs.inc.php index 482dd429c3738faea83e49df05a09c8dd391a7e2..5418518f71f498996aee68cd2a6f0b5a8f7b4034 100644 --- a/include/staff/syslogs.inc.php +++ b/include/staff/syslogs.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; if($_REQUEST['type']) { @@ -45,9 +45,10 @@ if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0)){ $qstr.='&endDate='.urlencode($_REQUEST['endDate']); } } -$sortOptions=array('title'=>'log.title','type'=>'log_type','ip'=>'log.ip_address','date'=>'log.created','created'=>'log.created','updated'=>'log.updated'); +$sortOptions=array('id'=>'log.log_id', 'title'=>'log.title','type'=>'log_type','ip'=>'log.ip_address' + ,'date'=>'log.created','created'=>'log.created','updated'=>'log.updated'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'date'; +$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'id'; //Sorting options... if($sort && $sortOptions[$sort]) { $order_column =$sortOptions[$sort]; diff --git a/include/staff/team.inc.php b/include/staff/team.inc.php index 7f8c409decba5de347d3cac28b848ce3ad78fa44..51b06ce2d1fa51344dbef35258a72cc68ddbf60b 100644 --- a/include/staff/team.inc.php +++ b/include/staff/team.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; if($team && $_REQUEST['a']!='add'){ diff --git a/include/staff/teams.inc.php b/include/staff/teams.inc.php index 1aa6dc4ed0554692b67449970caf0d799673f709..ab3a2f58d4a88e94ea4d08653b853352d37be680 100644 --- a/include/staff/teams.inc.php +++ b/include/staff/teams.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT team.*,count(m.staff_id) as members,CONCAT_WS(" ",lead.firstname,lead.lastname) as team_lead '. diff --git a/include/staff/template.inc.php b/include/staff/template.inc.php index 15881945e369c2fa541163008b3c9f4c9ce2352f..ac5c09a744879021e7286f116dc91e18a26df95f 100644 --- a/include/staff/template.inc.php +++ b/include/staff/template.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $info=array(); $qstr=''; diff --git a/include/staff/templates.inc.php b/include/staff/templates.inc.php index 7f82f656f12cafe984de35ff205eb62bfef2b1a7..f60e3b010ec7efe053fd1a73df1cefe9366b9daa 100644 --- a/include/staff/templates.inc.php +++ b/include/staff/templates.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; $sql='SELECT tpl.*,count(dept.tpl_id) as depts '. diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 33f46eadd443ccf0cda94867d0e54a924b73e30e..faac0f86ef791a8726c266a8121a980860ddc267 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -196,11 +196,11 @@ if($ticket->isOverdue()) </tr> <tr> <td colspan="2"> - <?php echo Format::htmlchars($note['note']); ?> + <?php echo Format::htmlchars($note['body']); ?> </td> </tr> <?php - if($note['attachments'] && ($links=$ticket->getAttachmentsLinks($note['note_id'],'N'))) {?> + if($note['attachments'] && ($links=$ticket->getAttachmentsLinks($note['id'],'N'))) {?> <tr> <td class="info" colspan="2"><?php echo $links; ?></td> </tr> @@ -220,9 +220,9 @@ if($ticket->isOverdue()) foreach($messages as $message) {?> <table class="message" cellspacing="0" cellpadding="1" width="940" border="0"> <tr><th><?php echo Format::db_datetime($message['created']); ?></th></tr> - <tr><td><?php echo Format::display($message['message']); ?></td></tr> + <tr><td><?php echo Format::display($message['body']); ?></td></tr> <?php - if($message['attachments'] && ($links=$ticket->getAttachmentsLinks($message['msg_id'],'M'))) {?> + if($message['attachments'] && ($links=$ticket->getAttachmentsLinks($message['id'],'M'))) {?> <tr> <td class="info"><?php echo $links; ?></td> </tr> @@ -231,17 +231,17 @@ if($ticket->isOverdue()) </table> <?php /* --------- Responses ------------ */ - if($message['responses'] && ($responses=$ticket->getResponses($message['msg_id']))) { + if($message['responses'] && ($responses=$ticket->getResponses($message['id']))) { foreach($responses as $resp) {?> <table class="response" cellspacing="0" cellpadding="1" width="100%" border="0"> <tr> <th><?php echo Format::db_datetime($resp['created']); ?> - <?php echo Format::htmlchars($resp['staff_name']); ?></th> </tr> <tr> - <td><?php echo Format::display($resp['response']); ?></td> + <td><?php echo Format::display($resp['body']); ?></td> </tr> <?php - if($resp['attachments'] && ($links=$ticket->getAttachmentsLinks($resp['response_id'],'R'))) {?> + if($resp['attachments'] && ($links=$ticket->getAttachmentsLinks($resp['id'],'R'))) {?> <tr> <td class="info"><?php echo $links; ?></td> </tr> @@ -251,7 +251,7 @@ if($ticket->isOverdue()) <?php } } - $msgId=$message['msg_id']; + $msgId=$message['id']; } } else { echo '<p>Error fetching ticket thread - get technical help.</p>'; diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 67d270c3f033e0db98e901d66b09fdcabf5247c6..9d6d5c7bcf0005e315f4e6e032943c97a237c01d 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -42,6 +42,7 @@ switch(strtolower($_REQUEST['status'])){ //Status is overloaded case 'assigned': $status='open'; $staffId=$thisstaff->getId(); + $results_type='My Tickets'; break; case 'answered': $status='open'; @@ -97,9 +98,6 @@ $deep_search=false; if($search): $qstr.='&a='.urlencode($_REQUEST['a']); $qstr.='&t='.urlencode($_REQUEST['t']); - if(isset($_REQUEST['advance_search'])){ //advance search box! - $qstr.='&advance_search=Search'; - } //query if($searchTerm){ @@ -108,6 +106,8 @@ if($search): if(is_numeric($searchTerm)){ $qwhere.=" AND ticket.ticketID LIKE '$queryterm%'"; }elseif(strpos($searchTerm,'@') && Validator::is_email($searchTerm)){ //pulling all tricks! + # XXX: What about searching for email addresses in the body of + # the thread message $qwhere.=" AND ticket.email='$queryterm'"; }else{//Deep search! //This sucks..mass scan! search anything that moves! @@ -117,33 +117,50 @@ if($search): $qwhere.=" AND ( ticket.email LIKE '%$queryterm%'". " OR ticket.name LIKE '%$queryterm%'". " OR ticket.subject LIKE '%$queryterm%'". - " OR note.title LIKE '%$queryterm%'". - " OR MATCH(message.message) AGAINST('$queryterm')". - " OR MATCH(response.response) AGAINST('$queryterm')". - " OR MATCH(note.note) AGAINST('$queryterm')". + " OR thread.title LIKE '%$queryterm%'". + " OR MATCH(thread.body) AGAINST('$queryterm')". ' ) '; }else{ $qwhere.=" AND ( ticket.email LIKE '%$queryterm%'". " OR ticket.name LIKE '%$queryterm%'". " OR ticket.subject LIKE '%$queryterm%'". - " OR message.message LIKE '%$queryterm%'". - " OR response.response LIKE '%$queryterm%'". - " OR note.note LIKE '%$queryterm%'". - " OR note.title LIKE '%$queryterm%'". + " OR thread.body LIKE '%$queryterm%'". + " OR thread.title LIKE '%$queryterm%'". ' ) '; } } } //department - if($_REQUEST['dept'] && in_array($_REQUEST['dept'],$thisstaff->getDepts())) { + if($_REQUEST['deptId'] && in_array($_REQUEST['deptId'],$thisstaff->getDepts())) { //This is dept based search..perm taken care above..put the sucker in. - $qwhere.=' AND ticket.dept_id='.db_input($_REQUEST['dept']); - $qstr.='&dept='.urlencode($_REQUEST['dept']); + $qwhere.=' AND ticket.dept_id='.db_input($_REQUEST['deptId']); + $qstr.='&deptId='.urlencode($_REQUEST['deptId']); } - //Teams - if($_REQUEST['team'] && ($thisuser->isadmin() || in_array($_REQUEST['team'],$thisuser->getTeams()))) { - $qwhere.=' AND ticket.team_id='.db_input($_REQUEST['team']); - $qstr.='&team='.urlencode($_REQUEST['team']); + + //Assignee + if($_REQUEST['assignee'] && strcasecmp($_REQUEST['status'], 'closed')) { + $id=preg_replace("/[^0-9]/", "", $_REQUEST['assignee']); + $assignee = $_REQUEST['assignee']; + $qstr.='&assignee='.urlencode($_REQUEST['assignee']); + $qwhere.= ' AND ( '; + + if($assignee[0]=='t') + $qwhere.=' (ticket.team_id='.db_input($id). ' AND ticket.status="open") '; + elseif($assignee[0]=='s') + $qwhere.=' (ticket.staff_id='.db_input($id). ' AND ticket.status="open") '; + else + $qwhere.=' (ticket.staff_id='.db_input($id). ' AND ticket.status="open") '; + + + if($_REQUEST['staffId'] && !$_REQUEST['status']) { //Assigned TO + Closed By + $qwhere.= ' OR (ticket.staff_id='.db_input($_REQUEST['staffId']). ' AND ticket.status="closed") '; + $qstr.='&staffId='.urlencode($_REQUEST['staffId']); + } + + $qwhere.= ' ) '; + } elseif($_REQUEST['staffId']) { + $qwhere.=' AND (ticket.staff_id='.db_input($_REQUEST['staffId']).' AND ticket.status="closed") '; + $qstr.='&staffId='.urlencode($_REQUEST['staffId']); } //dates @@ -163,7 +180,7 @@ if($search): $qwhere.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')'; $qstr.='&endDate='.urlencode($_REQUEST['endDate']); } -} + } endif; @@ -206,9 +223,7 @@ $qfrom=' FROM '.TICKET_TABLE.' ticket '. $sjoin=''; if($search && $deep_search) { - $sjoin=' LEFT JOIN '.TICKET_MESSAGE_TABLE.' message ON (ticket.ticket_id=message.ticket_id )' - .' LEFT JOIN '.TICKET_RESPONSE_TABLE.' response ON (ticket.ticket_id=response.ticket_id )' - .' LEFT JOIN '.TICKET_NOTE_TABLE.' note ON (ticket.ticket_id=note.ticket_id ) '; + $sjoin=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'; } $qgroup=' GROUP BY ticket.ticket_id'; @@ -222,9 +237,7 @@ $pageNav->setURL('tickets.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&ord //ADD attachment,priorities, lock and other crap $qselect.=' ,count(attach.attach_id) as attachments ' - .' ,count(DISTINCT message.msg_id) as messages ' - .' ,count(DISTINCT response.response_id) as responses ' - .' ,count(DISTINCT note.note_id) as notes ' + .' ,count(DISTINCT thread.id) as thread_count ' .' ,IF(ticket.reopened is NULL,IF(ticket.lastmessage is NULL,ticket.created,ticket.lastmessage),ticket.reopened) as effective_date ' .' ,CONCAT_WS(" ", staff.firstname, staff.lastname) as staff, team.name as team ' .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned '; @@ -233,9 +246,7 @@ $qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.pri .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW() AND tlock.staff_id!='.db_input($thisstaff->getId()).') ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (ticket.ticket_id=attach.ticket_id) ' - .' LEFT JOIN '.TICKET_MESSAGE_TABLE.' message ON (ticket.ticket_id=message.ticket_id) ' - .' LEFT JOIN '.TICKET_RESPONSE_TABLE.' response ON (ticket.ticket_id=response.ticket_id) ' - .' LEFT JOIN '.TICKET_NOTE_TABLE.' note ON (ticket.ticket_id=note.ticket_id ) ' + .' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON ( ticket.ticket_id=thread.ticket_id) ' .' LEFT JOIN '.STAFF_TABLE.' staff ON (ticket.staff_id=staff.staff_id) ' .' LEFT JOIN '.TEAM_TABLE.' team ON (ticket.team_id=team.team_id) '; @@ -245,17 +256,18 @@ $hash = md5($query); $_SESSION['search_'.$hash] = $query; $res = db_query($query); $showing=db_num_rows($res)?$pageNav->showing():""; -if(!$results_type) { - $results_type=($search)?'Search Results':ucfirst($status).' Tickets'; -} -$negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. +if(!$results_type) + $results_type = ucfirst($status).' Tickets'; -$basic_display=!isset($_REQUEST['advance_search'])?true:false; +if($search) + $results_type.= ' (Search Results)'; + +$negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. //YOU BREAK IT YOU FIX IT. ?> <!-- SEARCH FORM START --> -<div id='basic' style="display:<?php echo $basic_display?'block':'none'; ?>"> +<div id='basic_search'> <form action="tickets.php" method="get"> <input type="hidden" name="a" value="search"> <table> @@ -263,6 +275,7 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; <td><input type="text" id="basic-ticket-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>" autocomplete="off" autocorrect="off" autocapitalize="off"></td> <td><input type="submit" name="basic_search" class="button" value="Search"></td> + <td> <a href="" id="go-advanced">[advanced]</a></td> </tr> </table> </form> @@ -300,7 +313,7 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; title="Sort By Status <?php echo $negorder; ?>">Status</a></th> <?php } else { ?> - <th width="60" <?=$pri_sort?>> + <th width="60" <?php echo $pri_sort;?>> <a <?php echo $pri_sort; ?> href="tickets.php?sort=pri&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Priority <?php echo $negorder; ?>">Priority</a></th> <?php @@ -310,15 +323,18 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; <th width="150"> <a <?php echo $assignee_sort; ?> href="tickets.php?sort=assignee&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Assignee <?php echo $negorder;?>">Assigned To</a></th> - <?}elseif(!strcasecmp($status,'closed')){?> + <?php + } elseif(!strcasecmp($status,'closed')) { ?> <th width="150"> <a <?php echo $staff_sort; ?> href="tickets.php?sort=staff&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Closing Staff Name <?php echo $negorder; ?>">Closed By</a></th> - <?}else{?> + <?php + } else { ?> <th width="150"> - <a <?=$dept_sort?> href="tickets.php?sort=dept&order=<?=$negorder?><?=$qstr?>" - title="Sort By Department <?=$negorder?>">Department</a></th> - <?}?> + <a <?php echo $dept_sort; ?> href="tickets.php?sort=dept&order=<?php echo $negorder;?><?php echo $qstr; ?>" + title="Sort By Department <?php echo $negorder; ?>">Department</a></th> + <?php + } ?> </tr> </thead> <tbody> @@ -347,7 +363,7 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; } $tid=$row['ticketID']; $subject = Format::truncate($row['subject'],40); - $threadcount=$row['messages']+$row['responses']; + $threadcount=$row['thread_count']; if(!strcasecmp($row['status'],'open') && !$row['isanswered'] && !$row['lock_id']) { $tid=sprintf('<b>%s</b>',$tid); } @@ -449,3 +465,89 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false; } ?> </form> </div> +<div id="overlay"></div> +<div style="display:none;" id="advanced-search"> + <h3>Advanced Ticket Search</h3> + <a class="close" href="">×</a> + <form action="tickets.php" method="post" id="search" name="search"> + <input type="hidden" name="a" value="search"> + <fieldset class="query"> + <label for="query">Keyword:</label> + <input type="input" id="query" name="query" size="20"> <em>Optional</em> + </fieldset> + <fieldset> + <label for="status">Status:</label> + <select id="status" name="status"> + <option value="">— Any Status —</option> + <option value="open">Open</option> + <option value="overdue">Overdue</option> + <option value="closed">Closed</option> + </select> + <label for="deptId">Dept:</label> + <select id="deptId" name="deptId"> + <option value="">— All Departments —</option> + <?php + if(($mydepts = $thisstaff->getDepts()) && ($depts=Dept::getDepartments())) { + foreach($depts as $id =>$name) { + if(!in_array($id, $mydepts)) continue; + echo sprintf('<option value="%d">%s</option>', $id, $name); + } + } + ?> + </select> + </fieldset> + <fieldset class="owner"> + <label for="assignee">Assigned To:</label> + <select id="assignee" name="assignee"> + <option value="0">— Anyone —</option> + <?php + if(($users=Staff::getStaffMembers())) { + echo '<OPTGROUP label="Staff Members ('.count($users).')">'; + foreach($users as $id => $name) { + $k="s$id"; + echo sprintf('<option value="%s">%s</option>', $k, $name); + } + echo '</OPTGROUP>'; + } + + if(($teams=Team::getTeams())) { + echo '<OPTGROUP label="Teams ('.count($teams).')">'; + foreach($teams as $id => $name) { + $k="t$id"; + echo sprintf('<option value="%s">%s</option>', $k, $name); + } + echo '</OPTGROUP>'; + } + ?> + </select> + <label for="staffId">Closed By:</label> + <select id="staffId" name="staffId"> + <option value="0">— Anyone —</option> + <?php + if(($users=Staff::getStaffMembers())) { + foreach($users as $id => $name) + echo sprintf('<option value="%d">%s</option>', $id, $name); + } + ?> + </select> + </fieldset> + <fieldset class="date_range"> + <label>Date Range:</label> + <input class="dp" type="input" size="20" name="startDate"><i></i> + <span>TO</span> + <input class="dp" type="input" size="20" name="endDate"><i></i> + </fieldset> + <p> + <span class="buttons"> + <input type="submit" value="Search"> + <input type="reset" value="Reset"> + <input type="button" value="Cancel" class="close"> + </span> + <span class="spinner"> + <img src="./images/ajax-loader.gif" width="16" height="16"> + </span> + </p> + </form> + <div id="result-count"> + </div> +</div> diff --git a/include/staff/topic.inc.php b/include/staff/topic.inc.php index 6504c5443200a4a3bd82624fec18c27c0ff3ea24..f0f895409d99ebd2c39c4013572b1ee455887090 100644 --- a/include/staff/topic.inc.php +++ b/include/staff/topic.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isadmin()) die('Access Denied'); +if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $info=($_POST && $errors)?Format::input($_POST):array(); //Re-use the post info on error...savekeyboards.org if($topic && $_REQUEST['a']!='new'){ diff --git a/index.php b/index.php index ac4fe04a014455d083aacab299fdace3a045e529..db492a87048158ba17c55deb90e19ba167261997 100644 --- a/index.php +++ b/index.php @@ -26,23 +26,25 @@ require(CLIENTINC_DIR.'header.inc.php'); <div id="new_ticket"> <h3>Open A New Ticket</h3> - <form method="get" action="open.php"> - <div>Please provide as much detail as possible so we can best assist you. To update a previously submitted ticket, please login.</div> - <input type="submit" value="Open a New Ticket"> - </form> + <br> + <div>Please provide as much detail as possible so we can best assist you. To update a previously submitted ticket, please login.</div> + <p> + <a href="open.php" class="green button">Open a New Ticket</a> + </p> </div> <div id="check_status"> <h3>Check Ticket Status</h3> - <form class="status_form" action="view.php" method="get"> - <div>We provide archives and history of all your current and past support requests complete with responses.</div> - <input type="submit" value="Check Ticket Status"> - </form> + <br> + <div>We provide archives and history of all your current and past support requests complete with responses.</div> + <p> + <a href="view.php" class="blue button">Check Ticket Status</a> + </p> </div> </div> <div class="clear"></div> <?php -if($cfg && $cfg->isKnowledgebaseEnabled()){ +if($cfg && $cfg->isKnowledgebaseEnabled()){ //FIXME: provide ability to feature or select random FAQs ?? ?> <p>Be sure to browse our <a href="kb/index.php">Frequently Asked Questions (FAQs)</a>, before opening a ticket.</p> diff --git a/login.php b/login.php index 044d74644785f32a0f79bae76e6c4309fd473b2a..48dc9ec4aea9a5ab7c427cacc30b17c8005944b5 100644 --- a/login.php +++ b/login.php @@ -45,7 +45,7 @@ if($_POST && (!empty($_POST['lemail']) && !empty($_POST['lticket']))): //At this point we know the ticket is valid. //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? //Check the email given. - if($ticket->getId() && strcasecmp($ticket->getEMail(),$email)==0){ + if($ticket->getId() && strcasecmp($ticket->getEmail(),$email)==0){ //valid match...create session goodies for the client. $user = new ClientSession($email,$ticket->getId()); $_SESSION['_client']=array(); //clear. diff --git a/main.inc.php b/main.inc.php index bb997ebd6cb5d1d98d30136a66c55bd8a79008a7..7f8c2ba3e6ffa02eb499bc5f82ba1ca85e7fef31 100644 --- a/main.inc.php +++ b/main.inc.php @@ -54,8 +54,8 @@ /*############## Do NOT monkey with anything else beyond this point UNLESS you really know what you are doing ##############*/ #Current version && schema signature (Changes from version to version) - define('THIS_VERSION','1.7-DPR2'); //Shown on admin panel - define('SCHEMA_SIGNATURE','ssddsdsd'); //MD5 signature of the db schema. (used to trigger upgrades) + define('THIS_VERSION','1.7-DPR3'); //Shown on admin panel + define('SCHEMA_SIGNATURE','49478749dc680eef08b7954bd568cfd1'); //MD5 signature of the db schema. (used to trigger upgrades) #load config info $configfile=''; @@ -135,14 +135,12 @@ define('CANNED_ATTACHMENT_TABLE',TABLE_PREFIX.'canned_attachment'); define('TICKET_TABLE',TABLE_PREFIX.'ticket'); - define('TICKET_NOTE_TABLE',TABLE_PREFIX.'ticket_note'); - define('TICKET_MESSAGE_TABLE',TABLE_PREFIX.'ticket_message'); - define('TICKET_RESPONSE_TABLE',TABLE_PREFIX.'ticket_response'); + define('TICKET_THREAD_TABLE',TABLE_PREFIX.'ticket_thread'); define('TICKET_ATTACHMENT_TABLE',TABLE_PREFIX.'ticket_attachment'); define('TICKET_PRIORITY_TABLE',TABLE_PREFIX.'ticket_priority'); define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE); define('TICKET_LOCK_TABLE',TABLE_PREFIX.'ticket_lock'); - define('TICKET_HISTORY_TABLE',TABLE_PREFIX.'ticket_history'); + define('TICKET_EVENT_TABLE',TABLE_PREFIX.'ticket_event'); define('EMAIL_TABLE',TABLE_PREFIX.'email'); define('EMAIL_TEMPLATE_TABLE',TABLE_PREFIX.'email_template'); diff --git a/scp/admin.inc.php b/scp/admin.inc.php index a580fa6986b87661b039419bcf178e5ae46ad12e..5f6f5e7633422251ca7bbf8960f69461a8afbc35 100644 --- a/scp/admin.inc.php +++ b/scp/admin.inc.php @@ -15,34 +15,29 @@ **********************************************************************/ require('staff.inc.php'); //Make sure config is loaded and the staff is set and of admin type -if(!$cfg or !$thisstaff or !$thisstaff->isadmin()){ +if(!$cfg or !$thisstaff or !$thisstaff->isAdmin()){ header('Location: index.php'); require('index.php'); // just in case! exit; } //Some security related warnings - bitch until fixed!!! :) -if(defined('THIS_VERSION') && strcasecmp($cfg->getVersion(),THIS_VERSION)) { - $sysnotice=sprintf('The script is version %s while the database is version %s.',THIS_VERSION,$cfg->getVersion()); - if(file_exists('../setup/')) - $sysnotice.=' Possibly caused by incomplete <a href="../setup/upgrade.php">upgrade</a>.'; - $errors['err']=$sysnotice; -}elseif(!$cfg->isHelpDeskOffline()) { - if(file_exists('../setup/')){ - $sysnotice='Please take a minute to delete <strong>setup/install</strong> directory for security reasons.'; - }else{ - - if(CONFIG_FILE && file_exists(CONFIG_FILE) && is_writable(CONFIG_FILE)) { +if($cfg->isUpgradePending()) { + $errors['err']=$sysnotice='System upgrade is pending <a href="../setup/upgrade.php">Upgrade Now</a>'; +} elseif(!$cfg->isHelpDeskOffline()) { + + if(file_exists('../setup/')) { + $sysnotice='Please take a minute to delete <strong>setup/install</strong> directory (../setup/) for security reasons.'; + } elseif(CONFIG_FILE && file_exists(CONFIG_FILE) && is_writable(CONFIG_FILE)) { //Confirm for real that the file is writable by group or world. clearstatcache(); //clear the cache! $perms = @fileperms(CONFIG_FILE); if(($perms & 0x0002) || ($perms & 0x0010)) { $sysnotice=sprintf('Please change permission of config file (%s) to remove write access. e.g <i>chmod 644 %s</i>', - basename(CONFIG_FILE),basename(CONFIG_FILE)); + basename(CONFIG_FILE), basename(CONFIG_FILE)); } - } - } + if(!$sysnotice && ini_get('register_globals')) $sysnotice='Please consider turning off register globals if possible'; } diff --git a/scp/ajax.php b/scp/ajax.php index 471e9c710d577d662abae098ef2053dbfd702694..2b08168adda9664f710f8b4abaa47cb4c0b78801 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -43,10 +43,13 @@ $dispatcher = patterns('', url_get('^ticket_variables', 'ticket_variables') )), url('^/config/', patterns('ajax.config.php:ConfigAjaxAPI', - url_get('^ui', 'ui') + url_get('^ui', 'scp_ui') )), url_get('^/users$', array('ajax.users.php:UsersAjaxAPI', 'search')), - url_get('^/tickets$', array('ajax.tickets.php:TicketsAjaxAPI', 'search')), + url('^/tickets', patterns('ajax.tickets.php:TicketsAjaxAPI', + url_get('^/lookup', 'lookup'), + url_get('^$', 'search') + )), url('^/ticket/', patterns('ajax.tickets.php:TicketsAjaxAPI', url_get('^(?P<tid>\d+)/preview', 'previewTicket'), url_get('^(?P<tid>\d+)/lock', 'acquireLock'), diff --git a/scp/banlist.php b/scp/banlist.php index 1ef580a5746e7666c7aebf4f72e364acb30456ea..b56d05c6d07b28b2536127abe9a5b3be6330cb69 100644 --- a/scp/banlist.php +++ b/scp/banlist.php @@ -68,7 +68,7 @@ if($_POST && !$errors && $filter){ }else{ $count=count($_POST['ids']); if($_POST['enable']){ - $sql='UPDATE '.EMAIL_FILTER_RULE_TABLE.' SET isactive=1 WHERE filter_id='.db_input($filter->getID()). + $sql='UPDATE '.EMAIL_FILTER_RULE_TABLE.' SET isactive=1 WHERE filter_id='.db_input($filter->getId()). ' AND id IN ('.implode(',',$_POST['ids']).')'; if(db_query($sql) && ($num=db_affected_rows())){ if($num==$count) @@ -79,7 +79,7 @@ if($_POST && !$errors && $filter){ $errors['err']='Unable to enable selected emails'; } }elseif($_POST['disable']){ - $sql='UPDATE '.EMAIL_FILTER_RULE_TABLE.' SET isactive=0 WHERE filter_id='.db_input($filter->getID()). + $sql='UPDATE '.EMAIL_FILTER_RULE_TABLE.' SET isactive=0 WHERE filter_id='.db_input($filter->getId()). ' AND id IN ('.implode(',',$_POST['ids']).')'; if(db_query($sql) && ($num=db_affected_rows())) { if($num==$count) diff --git a/scp/css/scp.css b/scp/css/scp.css index 5ccdbe543b0030b0fe910fc2382102bd997a4acc..c5eb222ba8bb06f9111b068fffb23cd7a9cb17c3 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -1,1062 +1,1308 @@ -body { - background:#eee; - font-family:arial, helvetica, sans-serif; - font-size:10pt; - color:#000; - margin:0; - padding:0; -} - -a { - color:#E65524; - text-decoration:none; -} - -.centered { - text-align:center; -} - -.clear { - clear:both; -} - -.faded { - color:#666; -} - -.strike { text-decoration:line-through; color:red; } - -#canned_attachments label { padding:3px; padding-right:10px; } - - -#breadcrumbs { - color: #333; - margin-bottom: 15px; -} - -#breadcrumbs a { - color: #555; -} - -#msg_notice { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #0a0; background: url('../images/icons/ok.png?1300763726') 10px 50% no-repeat #e0ffe0; } - -#msg_warning { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #f26522; background: url('../images/icons/alert.png?1307823786') 10px 50% no-repeat #ffffdd; } - -#msg_error { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #a00; background: url('../images/icons/error.png') 10px 50% no-repeat #fff0f0; } - - -#container { - width:960px; - margin:0 auto 20px auto; -} - -#header { - height:76px; - background:url(../images/header-bg.png) top left repeat-x; - border-left:1px solid #aaa; - border-right:1px solid #aaa; -} - -#logo { - display:block; - float:left; - width:190px; - height:76px; - text-decoration:none; - outline:none; - text-indent:-9999px; - background:url(../images/ost-logo.png) top left no-repeat; -} - -#header p { - display:block; - width:430px; - float:right; - margin:10px; - background:#eee; - border:1px solid #ccc; - padding:8px; - text-align:center; -} - -#nav, #sub_nav { - clear:both; - margin:0; - padding:0 20px; - height:26px; - line-height:26px; - border-left:1px solid #aaa; - border-right:1px solid #aaa; -} - -#nav .active, #sub_nav li { - margin:0; - padding:0; - list-style:none; - display:inline; -} - -#nav { - background:#eee; - padding-top:4px; - z-index:200; - border-top:1px solid #ddd; - border-bottom:1px solid #c5d9ec; -} - -#nav .active a, #nav .inactive { - display:block; - float:left; - width:115px; - height:26px; - color:#555; - text-align:center; - font-weight:bold; - margin-top:1px; - margin-right:5px; - position:relative; -} - -#nav .inactive a { - color:#555; - display:block; -} - -#nav .active a { - background:url(../images/tab-bg.png) top left no-repeat; - color:#004a80; -} - -#nav .inactive ul { - display:none; - width:230px; - background:#fbfbfb; - margin:0; - padding:0; - position:relative; - z-index:500; - border-bottom:1px solid #ccc; - border-left:1px solid #ccc; - border-right:1px solid #ccc; -} - -#nav .inactive li { - display:block; - margin:0; - padding:0 5px; - list-style:none; - text-align:left; -} - -#nav .inactive:hover { - background:url(../images/tab-bg.png) bottom left no-repeat; -} - -#nav .inactive:hover ul { - display:block; - -moz-box-shadow: 3px 3px 3px #ccc; - -webkit-box-shadow: 3px 3px 3px #ccc; - box-shadow: 3px 3px 3px #ccc; -} - -.ieshadow { - width:230px; - background:#000; - filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.30); - -ms-filter: "progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.30)"; - zoom: 1; - z-index:300; - position:absolute; - top:24px; - left:0; -} - -#nav .inactive li { - background:#fbfbfb; -} - -#nav .inactive li a { - padding-left:24px; - background-position:0 50%; - background-repeat:no-repeat; - font-weight:normal; - background-color:#fbfbfb; -} - -#nav .inactive li a:hover { - color:#E65524; -} - -#sub_nav { - background:#f7f7f7; - border-bottom:1px solid #bebebe; -} - -#sub_nav a { - display:block; - float:left; - margin-right:10px; - padding:0 10px 0 21px; - background-position:0 50%; - background-repeat:no-repeat; - color:#000; -} - -#sub_nav a:hover { - color:#E65524; -} - -#sub_nav a.active { - font-weight:bold; -} - -#sub_nav .open { background-image:url(../images/icons/open.gif) } -#sub_nav .answered { background-image:url(../images/icons/answered.gif) } -#sub_nav .mine { background-image:url(../images/icons/mine.gif) } -#sub_nav .closed { background-image:url(../images/icons/closed.gif) } -#sub_nav .new { background-image:url(../images/icons/new.gif) } - -a.test { background-image:url(../images/icons/open.gif) } - -a.Ticket { background:url(../images/icons/open_tickets.gif) } -a.assignedTickets { background:url(../images/icons/assigned_tickets.gif) } -a.overdueTickets { background:url(../images/icons/overdue_tickets.gif) } -a.answeredTickets { background:url(../images/icons/answered_tickets.gif) } -a.closedTickets { background:url(../images/icons/closed_tickets.gif) } -a.newTicket { background:url(../images/icons/new_ticket.gif) } - -a.premade { background:url(../images/icons/premade_reply.gif) } -a.newPremade { background:url(../images/icons/new_premade_reply.gif) } - -a.staff { background:url(../images/icons/list_groups.gif) } -a.user { background:url(../images/icons/list_users.gif) } -a.userPref { background:url(../images/icons/user_preferences.gif) } -a.userPasswd { background:url(../images/icons/change_password.gif) } - -a.preferences { background:url(../images/icons/settings.gif) } -a.attachment { background:url(../images/icons/attachment.gif ) } -a.api { background:url(../images/icons/api.png) } -a.newapi { background:url(../images/icons/new_api.png) } - -a.sla { background:url(../images/icons/slas.png) } -a.newsla { background:url(../images/icons/new_sla.png) } - -a.logs { background:url(../images/icons/logs.gif) } - -a.emails { background:url(../images/icons/emails.png) } -a.newEmail { background:url(../images/icons/new_email.png) } - -a.emailTemplates { background:url(../images/icons/email_templates.png) } -a.newEmailTemplate { background:url(../images/icons/new_email_template.png) } - -a.emailFilters { background:url(../images/icons/email_filters.png) } -a.newEmailFilter { background:url(../images/icons/new_email_filter.png) } - -a.emailSettings { background:url(../images/icons/emails.png) } -a.emailDiagnostic { background:url(../images/icons/email_diagnostic.gif) } -a.banList { background:url(../images/icons/ban_list.gif) } - -a.users { background:url(../images/icons/list_users.gif) } -a.newuser { background:url(../images/icons/new_user.gif) } -a.groups { background:url(../images/icons/list_groups.gif) } -a.teams { background:url(../images/icons/teams.gif) } -a.newgroup { background:url(../images/icons/new_group.gif) } - -a.helpTopics { background:url(../images/icons/help_topics.png) } -a.newHelpTopic { background:url(../images/icons/new_help_topic.png) } - -a.departments { background:url(../images/icons/list_departments.gif) } -a.newDepartment { background:url(../images/icons/new_department.gif) } - - -/* Generic CSS based Icons. use=> <tag class="Icon iconname">text</tag> */ - -.Icon { - width: auto; - padding-left:20px; - background-position: left center; - background-repeat: no-repeat; -} - - -a.Icon { background-repeat: no-repeat;} - - -a.Icon:hover { - text-decoration: underline; -} - - -.Icon.newstaff { background:url(../images/icons/new_user.gif) 0 0 no-repeat; } -.Icon.newteam { background:url(../images/icons/new_team.gif) 0 0 no-repeat; } - -.Icon.Ticket { background:url(../images/icons/ticket.gif) 0 2px no-repeat; } -.Icon.webTicket { background:url(../images/icons/ticket_source_web.gif) 0 0 no-repeat; } -.Icon.emailTicket { background:url(../images/icons/ticket_source_email.gif) 0 0 no-repeat; } -.Icon.phoneTicket { background:url(../images/icons/ticket_source_phone.gif) 0 0 no-repeat; } -.Icon.otherTicket { background:url(../images/icons/ticket_source_other.gif) 0 0 no-repeat; } -.Icon.overdueTicket { background:url(../images/icons/overdue_ticket.gif) 0 0 no-repeat; } -.Icon.assignedTicket { background:url(../images/icons/assigned_ticket.gif) 0 0 no-repeat; } -.Icon.lockedTicket { background:url(../images/icons/locked_ticket.gif) 0 0 no-repeat; } -.Icon.editTicket { background-image: url(../images/icons/edit_ticket.png); } - -.Icon.file { background-image: url(../images/icons/file.gif); } -.Icon.refresh { background-image: url(../images/icons/refresh.gif); } -.Icon.note { - font-weight: bold; - font-size: 1em; - background-image: url(../images/icons/note.gif); -} - -.Icon.thread { - font-weight: bold; - font-size: 1em; - background-image: url(../images/icons/thread.gif); -} - - -.Icon.debugLog { background:url(../images/icons/log_debug.gif) 0 2px no-repeat; } -.Icon.alertLog { background:url(../images/icons/log_alert.gif) 0 2px no-repeat; } -.Icon.errorLog { background:url(../images/icons/log_error.gif) 0 2px no-repeat; } - - - -#content { - clear:both; - border:1px solid #aaa; - border-top:none; - border-bottom:3px solid #bbb; - padding:10px 10px 20px 10px; - background:#fff; -} - -#content a { - color:#184E81; -} - -#footer { - clear:both; - padding:10px; - text-align:center; - font-size:9pt; -} - -table { vertical-align:top; } - -table.list { - clear:both; - background:#ccc; - margin: 2px 0; - border-bottom: 1px solid #ccc; - font-family:arial, helvetica, sans-serif; - font-size:10pt; -} - -table.list caption { - text-align:left; - padding:5px; - background:#929292; - color:#fff; - font-weight:bold; -} - -table.list thead th { - background-color:#eee; - color:#000; - text-align:left; - vertical-align:top; -} - -table.list th a { - - text-decoration:none; - color:#000; -} - -table.list thead th a { padding: 3px; display: block; color: #000; background: url('../images/asc_desc.gif') 100% 50% no-repeat; } - -table.list thead th a.asc { background: url('../images/asc.gif') 100% 50% no-repeat #cfe6ff; } -table.list thead th a.desc { background: url('../images/desc.gif') 100% 50% no-repeat #cfe6ff; } -table.list tbody td { - background:#fff; - border:1px solid #fff; - padding:1px; - vertical-align:top; -} - -table.list tbody td { background: #fff; padding: 1px; padding-left:2px; vertical-align: top; } -table.list tbody tr.odd td { background-color: #f0faff; } -table.list tbody tr:hover td { background: #ffe; } -table.list tbody tr.odd:hover td { background: #ffd; } - -table.list tfoot td { - background:#eee; - padding: 2px; -} - -table.list tbody td.webticket, table.list tbody tr.row1 td.webticket { - text-indent:20px; - background:url(../images/icons/ticket_source_web.gif) 0 50% no-repeat #fff; -} - -table.list tbody td.emailticket, table.list tbody tr.row1 td.emailticket { - text-indent:20px; - background:url(../images/icons/ticket_source_email.gif) 0 50% no-repeat; -} - -table.list tbody td.phoneticket, table.list tbody tr.row1 td.phoneticket { - text-indent:20px; - background:url(../images/icons/ticket_source_phone.gif) 0 50% no-repeat; -} - -table.list tbody td.otherticket, table.list tbody tr.row1 td.otherticket { - text-indent:20px; - background:url(../images/icons/ticket_source_other.gif) 0 50% no-repeat; -} - -a.refresh { - display:block; - float:right; - width:auto; - height:16px; - line-height:16px; - padding:2px 5px 2px 2px; - background-position:2px 50%; - background-repeat:no-repeat; - padding-left:24px; - margin-left:10px; - margin-bottom: 2px; - border:1px solid #aaa; - background-image:url(../images/icons/refresh.gif); -} - -a.edit, a.print { - display:block; - float:right; - width:auto; - height:16px; - line-height:16px; - padding:2px 5px 2px 2px; - background-position:2px 50%; - background-repeat:no-repeat; - padding-left:24px; - margin-left:10px; - border:1px solid #aaa; - background-image:url(../images/icons/edit_ticket.png); -} - -a.print { - background-image:url(../images/icons/printer.gif); -} - -.btn { - padding:3px 10px; - background:url(../images/btn_bg.png) top left repeat-x #ccc; - border:1px solid #777; - color:#000; -} - -.button { padding:1px 5px; margin-right:10px; color:#777; font-weight:bold;} - -.btn_sm { - padding:2px 5px; - font-size:9pt; - background:url(../images/btn_sm_bg.png) top left repeat-x #f90; - border:1px solid #777; - color:#fff; - font-weight:bold; -} - -.btn:hover, .btn_sm:hover { - background-position: bottom left; -} - -.search label { - display:block; - line-height:25px; - height:25px; -} - -.search input[type=text] { - height:23px; - line-height:23px; - border:1px solid #aaa; - background:#fff; - padding:2px; -} - -.form_table { - margin-top:3px; - border-left:1px solid #ddd; - border-right:1px solid #ddd; -} - -.form_table td { - border-bottom:1px solid #ddd; -} - - -.form_table td.multi-line { - vertical-align:top; -} - -.form_table input[type=text], .form_table input[type=password], .form_table textarea { - background:#fff; - border:1px solid #aaa; -} - -.form_table input[type=radio], .form_table input[type=checkbox] { - position:relative; - top:3px; - margin-left:0; - padding-left:0; -} - -.form_table .required { - font-weight:bold; -} - -.form_table em { - font-weight:normal; - color:#666; -} - -.error { - color:#f00; -} - -.form_table .error input { - border:1px solid #f00; -} - -.form_table th { - text-align:left; - border:1px solid #ccc; - background:#eee; - padding:0; -} - -.form_table th h4 { - margin:0; - padding:5px; - color:#fff; - background:#929292; -} - -.form_table th em { - display:block; - padding:5px; - color:#000; -} - -.settings_table { - margin-top:2px; - border-left:1px solid #ddd; - border-right:1px solid #ddd; -} - -.settings_table td { - border-bottom:1px solid #ddd; -} - -.settings_table input[type=radio], .settings_table input[type=checkbox] { - margin-left:0; - padding-left:0; -} - -#content .settings_table th h4 a { - display:block; - color:#fff; -} - -.settings_table h4 a span { - font-size:12pt; - line-height:14px; - display:inline-block; - width:14px; - height:14px; - overflow:hidden; - text-align:center; - color:#444; - background:#ccc; - position:relative; - top:2px; -} - -h2 { - margin:0; - padding:0; - font-size:12pt; - color:#0A568E; -} - -h2 span { color:#000; } - -h3 { - margin:10px 0 0 0; - padding:5px 0; - font-size:10pt; - color:#444; -} - -.ticket_info th { - text-align:left; -} - -.ticket_info { - background:#F4FAFF; -} - -.right_align { text-align:right; } - -h2 .reload { - display:inline-block; - width:16px; - height:16px; - background:url(../images/icons/refresh.gif) top left no-repeat; - outline:none; - text-indent:-9999px; -} - -#assigned_message { - margin:10px 0; - padding:5px 5px 5px 30px; - background:url(../images/icons/assigned_ticket.gif) 5px 50% no-repeat #ffd; - border:1px solid #f90; -} - - - - -#ticket_actions { - padding:5px; - background:#eee; - border:1px solid #aaa; - border-bottom:none; - margin:0; -} - -#threads { - margin:0; - padding:5px 10px 0 10px; - border:1px solid #aaa; - background:#F4FAFF; - height:30px; -} - -#threads li { - list-style:none; - margin:0; - padding:0; - display:inline; -} - -#threads li a { - display:block; - width:auto; - float:left; - height:30px; - line-height:30px; - border-top:1px solid #F4FAFF; - padding:0 10px 0 32px; - margin-right:10px; -} - -#threads li a.active { - height:29px; - background-color:#fff; - border:1px solid #aaa; - border-bottom:none; - border-top:2px solid #ed9100; - font-weight:bold; -} - -#toggle_ticket_thread { - background:url(../images/icons/open.gif) 10px 50% no-repeat; -} - -#toggle_notes { - background:url(../images/icons/note.gif) 10px 50% no-repeat; -} - -#latest_notes { - margin:10px 0; - padding:10px; - background:#ffe; - border:1px solid #e7e765; -} - -#latest_notes h3 { - margin:0 0 10px 0; - padding:0; - font-size:11pt; -} - -#latest_notes h3 span, #latest_notes h3 a { - color:#777; - font-weight:normal; - text-decoration:none; - font-size:10pt; -} - -#latest_notes ul { - margin:0 20px; - padding:0; -} - -#latest_notes ul li { - margin:0; - padding:0 0 10px 0; - list-style:none; -} - -#latest_notes em { - color:#777; -} - -#ticket_thread table { - margin-top:10px; - border:1px solid #aaa; - border-bottom:2px solid #aaa; -} - -#ticket_notes table { - margin-top:10px; - border:1px solid #ddd; - border-bottom:2px solid #ddd; -} - -#ticket_thread table th, #ticket_notes table th { - text-align:left; - border-bottom:1px solid #aaa; - font-size:10pt; - padding:5px; -} - -#ticket_notes table th { - text-align:left; - border-bottom:1px solid #ddd; - font-size:10pt; - padding:5px; - background:#F4FAFF; -} - -#ticket_notes table th em { - font-weight:normal; - font-size:10pt; - color:#666; -} - -#ticket_thread .message th { - background:#C3D9FF; -} - -#ticket_notes .date { - font-weight:normal; - font-size:10pt; - color:#888; - text-align:right; -} - -#ticket_thread .response th { - background:#FFE0B3; -} - -#ticket_thread table td, #ticket_notes table td { - padding:5px; -} - -#ticket_notes td { - background:#f9f9f9; -} - -#ticket_thread .info, #ticket_notes .info { - padding:5px; - background:#F4FAFF; - height:16px; - line-height:16px; -} - -#ticket_notes .info { - background:#f9f9f9; -} - -#response_options { - margin-top:30px; -} - -#response_options form { - padding:0 10px; -} - -#response_options ul { - padding:4px 0 0 190px; - margin:0; - text-align:center; - height:29px; - border-bottom:1px solid #aaa; - background:#eef3f8; -} - -#response_options li { - margin:0; - padding:0; - display:inline; - list-style:none; -} - -#response_options li a { - width:130px; - font-weight:bold; - padding:5px; - height:18px; - line-height:20px; - color:#444; - display:block; - float:left; - outline:none; - position:relative; - top:0; - background:#fbfbfb; - border:1px solid #eee; - border-bottom:none; -} - -#response_options .reply_tab.tell { - color:#a00 !important; - background-image:url(../images/reminder.png); - background-position:12px 50%; - background-repeat:no-repeat; -} - -#response_options li a.active { - height:18px; - color:#184E81; - background-color:#f9f9f9; - border:1px solid #aaa; - border-top:2px solid #81a9d7; - border-bottom:none; -} - -#response_options form { - padding:10px 5px; - background:#f9f9f9; - border:1px solid #aaa; - border-top:none; -} - -#response_options table { - width:928px; -} - -#response_options td { - vertical-align:top; -} - -#response_options textarea { - width:760px !important; -} - -#response_options input[type=text], #response_options textarea { - border:1px solid #aaa; - background:#fff; -} - -.attachments .uploads div { - display:inline-block; - padding-right:20px; -} - - - -.file { - display:inline-block; - padding-left:20px; - margin-right:20px; - background:url(../images/icons/file.gif) 0 50% no-repeat; -} - -.expander { - line-height:14px; - display:inline-block; - width:12px; - height:12px; - overflow:hidden; - text-align:center; - color:#aaa; - position:relative; -} - -/** Popup Tool Tips and Content **/ - -.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; - -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; - width:auto !important; - width:300px; -} - -.tip_content hr { - - color: #ddd; - background-color: #ddd; - height: 1px; - border: 0; - padding: 0; - margin: 0.2em 0; - width: 100%; -} - -.tip_close { - position:absolute; - left:100%; - top:0; - margin-left:-12px; -} - -.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_menu { - margin:10px 0 0 0; - padding:5px 0; - border-top:1px solid #aaa; - height:16px; - font-size:9pt; -} - -.tip_menu li { - display:inline; - list-style:none; - margin:0; - padding:0; -} - -.tip_menu li a { - display:block; - width:auto; - _width:0; - float:left; - padding:0 10px; - border-right:1px solid #ddd; - color:#666; -} - -.tip_menu li a:hover { - color:#E76C74; -} - -.tip_content form { - display:none; - line-height:24px; -} - -.tip_content select, .tip_content textarea { - width:295px; -} - -.tip_content textarea { - padding:0; - border:1px solid #aaa; - background:#fff; -} - -.tip_content form p { - margin:0; - width:auto !important; - width:295px; - text-align:right; - line-height:24px; -} - -/* Knowledgebase */ -#kb { - margin: 2px 0; - padding: 0; - overflow: hidden; -} - -#kb > li { - margin: 0 0 5px 0; - padding: 0 10px; - width: auto; - float: left; - clear: both; -} - -#kb > li h4 { - padding-bottom:3px; - margin-bottom:3px; -} - -#kb > li h4 span { - color:#666; -} - -#kb > li h4 a { - font-size: 14px; -} - -#faq { - clear: both; - margin: 0; - padding: 5 0 10px 5px; -} -#faq ol { - font-size: 15px; - margin-left: 0; - padding-left: 0; -} -#faq ol li { - list-style: none; - margin: 0 0 10px 0; - color: #999; -} -#faq ol li a { - display: inline; - height: 16px; - line-height: 16px; - padding-left: 24px; - background: url('../images/icons/page.png') 0 50% no-repeat; -} +body { + background:#eee; + font-family:arial, helvetica, sans-serif; + font-size:10pt; + color:#000; + margin:0; + padding:0; +} + +a { + color:#E65524; + text-decoration:none; +} + +.centered { + text-align:center; +} + +.clear { + clear:both; +} + +.faded { + color:#666; +} + +.strike { text-decoration:line-through; color:red; } + +#canned_attachments label { padding:3px; padding-right:10px; } + + +#breadcrumbs { + color: #333; + margin-bottom: 15px; +} + +#breadcrumbs a { + color: #555; +} + +#msg_notice { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #0a0; background: url('../images/icons/ok.png?1300763726') 10px 50% no-repeat #e0ffe0; } + +#msg_warning { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #f26522; background: url('../images/icons/alert.png?1307823786') 10px 50% no-repeat #ffffdd; } + +#msg_error { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #a00; background: url('../images/icons/error.png') 10px 50% no-repeat #fff0f0; } + + +#container { + width:960px; + margin:0 auto 20px auto; +} + +#header { + height:76px; + background:url(../images/header-bg.png) top left repeat-x; + border-left:1px solid #aaa; + border-right:1px solid #aaa; +} + +#logo { + display:block; + float:left; + width:190px; + height:76px; + text-decoration:none; + outline:none; + text-indent:-9999px; + background:url(../images/ost-logo.png) top left no-repeat; +} + +#header p { + display:block; + width:430px; + float:right; + margin:10px; + background:#eee; + border:1px solid #ccc; + padding:8px; + text-align:center; +} + +#nav, #sub_nav { + clear:both; + margin:0; + padding:0 20px; + height:26px; + line-height:26px; + border-left:1px solid #aaa; + border-right:1px solid #aaa; +} + +#nav .active, #sub_nav li { + margin:0; + padding:0; + list-style:none; + display:inline; +} + +#nav { + background:#eee; + padding-top:4px; + z-index:200; + border-top:1px solid #ddd; + border-bottom:1px solid #c5d9ec; +} + +#nav .active a, #nav .inactive { + display:block; + float:left; + width:115px; + height:26px; + color:#555; + text-align:center; + font-weight:bold; + margin-top:1px; + margin-right:5px; + position:relative; +} + +#nav .inactive a { + color:#555; + display:block; +} + +#nav .active a { + background:url(../images/tab-bg.png) top left no-repeat; + color:#004a80; +} + +#nav .inactive ul { + display:none; + width:230px; + background:#fbfbfb; + margin:0; + padding:0; + position:relative; + z-index:500; + border-bottom:1px solid #ccc; + border-left:1px solid #ccc; + border-right:1px solid #ccc; +} + +#nav .inactive li { + display:block; + margin:0; + padding:0 5px; + list-style:none; + text-align:left; +} + +#nav .inactive:hover { + background:url(../images/tab-bg.png) bottom left no-repeat; +} + +#nav .inactive:hover ul { + display:block; + -moz-box-shadow: 3px 3px 3px #ccc; + -webkit-box-shadow: 3px 3px 3px #ccc; + box-shadow: 3px 3px 3px #ccc; +} + +.ieshadow { + width:230px; + background:#000; + filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.30); + -ms-filter: "progid:DXImageTransform.Microsoft.Blur(PixelRadius=3,MakeShadow=true,ShadowOpacity=0.30)"; + zoom: 1; + z-index:300; + position:absolute; + top:24px; + left:0; +} + +#nav .inactive li { + background:#fbfbfb; +} + +#nav .inactive li a { + padding-left:24px; + background-position:0 50%; + background-repeat:no-repeat; + font-weight:normal; + background-color:#fbfbfb; +} + +#nav .inactive li a:hover { + color:#E65524; +} + +#sub_nav { + background:#f7f7f7; + border-bottom:1px solid #bebebe; +} + +#sub_nav a { + display:block; + float:left; + margin-right:10px; + padding:0 10px 0 21px; + background-position:0 50%; + background-repeat:no-repeat; + color:#000; +} + +#sub_nav a:hover { + color:#E65524; +} + +#sub_nav a.active { + font-weight:bold; +} + +#sub_nav .open { background-image:url(../images/icons/open.gif) } +#sub_nav .answered { background-image:url(../images/icons/answered.gif) } +#sub_nav .mine { background-image:url(../images/icons/mine.gif) } +#sub_nav .closed { background-image:url(../images/icons/closed.gif) } +#sub_nav .new { background-image:url(../images/icons/new.gif) } + +a.test { background-image:url(../images/icons/open.gif) } + +a.Ticket { background:url(../images/icons/open_tickets.gif) } +a.assignedTickets { background:url(../images/icons/assigned_tickets.gif) } +a.overdueTickets { background:url(../images/icons/overdue_tickets.gif) } +a.answeredTickets { background:url(../images/icons/answered_tickets.gif) } +a.closedTickets { background:url(../images/icons/closed_tickets.gif) } +a.newTicket { background:url(../images/icons/new_ticket.gif) } + +a.premade { background:url(../images/icons/premade_reply.gif) } +a.newPremade { background:url(../images/icons/new_premade_reply.gif) } + +a.kb { background:url(../images/icons/kb.gif) } +a.kb-categories { background:url(../images/icons/kb-categories.gif) } +a.canned { background:url(../images/icons/canned.gif) } + +a.staff { background:url(../images/icons/list_groups.gif) } +a.user { background:url(../images/icons/list_users.gif) } +a.userPref { background:url(../images/icons/user_preferences.gif) } +a.userPasswd { background:url(../images/icons/change_password.gif) } + +a.preferences { background:url(../images/icons/settings.gif) } +a.attachment { background:url(../images/icons/attachment.gif ) } +a.api { background:url(../images/icons/api.png) } +a.newapi { background:url(../images/icons/new_api.png) } + +a.sla { background:url(../images/icons/slas.png) } +a.newsla { background:url(../images/icons/new_sla.png) } + +a.logs { background:url(../images/icons/logs.gif) } + +a.emails { background:url(../images/icons/emails.png) } +a.newEmail { background:url(../images/icons/new_email.png) } + +a.emailTemplates { background:url(../images/icons/email_templates.png) } +a.newEmailTemplate { background:url(../images/icons/new_email_template.png) } + +a.emailFilters { background:url(../images/icons/email_filters.png) } +a.newEmailFilter { background:url(../images/icons/new_email_filter.png) } + +a.emailSettings { background:url(../images/icons/emails.png) } +a.emailDiagnostic { background:url(../images/icons/email_diagnostic.gif) } +a.banList { background:url(../images/icons/ban_list.gif) } + +a.users { background:url(../images/icons/list_users.gif) } +a.newuser { background:url(../images/icons/new_user.gif) } +a.groups { background:url(../images/icons/list_groups.gif) } +a.teams { background:url(../images/icons/teams.gif) } +a.newgroup { background:url(../images/icons/new_group.gif) } + +a.helpTopics { background:url(../images/icons/help_topics.png) } +a.newHelpTopic { background:url(../images/icons/new_help_topic.png) } + +a.departments { background:url(../images/icons/list_departments.gif) } +a.newDepartment { background:url(../images/icons/new_department.gif) } + + +/* Generic CSS based Icons. use=> <tag class="Icon iconname">text</tag> */ + +.Icon { + width: auto; + padding-left:20px; + background-position: left center; + background-repeat: no-repeat; +} + + +a.Icon { background-repeat: no-repeat;} + + +a.Icon:hover { + text-decoration: underline; +} + + +.Icon.newstaff { background:url(../images/icons/new_user.gif) 0 0 no-repeat; } +.Icon.newteam { background:url(../images/icons/new_team.gif) 0 0 no-repeat; } + +.Icon.Ticket { background:url(../images/icons/ticket.gif) 0 2px no-repeat; } +.Icon.webTicket { background:url(../images/icons/ticket_source_web.gif) 0 0 no-repeat; } +.Icon.emailTicket { background:url(../images/icons/ticket_source_email.gif) 0 0 no-repeat; } +.Icon.phoneTicket { background:url(../images/icons/ticket_source_phone.gif) 0 0 no-repeat; } +.Icon.otherTicket { background:url(../images/icons/ticket_source_other.gif) 0 0 no-repeat; } +.Icon.overdueTicket { background:url(../images/icons/overdue_ticket.gif) 0 0 no-repeat; } +.Icon.assignedTicket { background:url(../images/icons/assigned_ticket.gif) 0 0 no-repeat; } +.Icon.lockedTicket { background:url(../images/icons/locked_ticket.gif) 0 0 no-repeat; } +.Icon.editTicket { background-image: url(../images/icons/edit_ticket.png); } + +.Icon.newCategory { background-image: url(../images/icons/new_category.png); } +.Icon.editCategory { background-image: url(../images/icons/edit_category.png); } +.Icon.deleteCategory { background-image: url(../images/icons/delete_category.png); } +.Icon.newFAQ { background-image: url(../images/icons/new_faq.png); } +.Icon.newReply { background-image: url(../images/icons/new_reply.png); } + +.Icon.file { background-image: url(../images/icons/file.gif); } +.Icon.refresh { background-image: url(../images/icons/refresh.gif); } +.Icon.note { + font-weight: bold; + font-size: 1em; + background-image: url(../images/icons/note.gif); +} + +.Icon.thread { + font-weight: bold; + font-size: 1em; + background-image: url(../images/icons/thread.gif); +} + + +.Icon.debugLog { background:url(../images/icons/log_debug.gif) 0 2px no-repeat; } +.Icon.alertLog { background:url(../images/icons/log_alert.gif) 0 2px no-repeat; } +.Icon.errorLog { background:url(../images/icons/log_error.gif) 0 2px no-repeat; } + + + +#content { + clear:both; + border:1px solid #aaa; + border-top:none; + border-bottom:3px solid #bbb; + padding:10px 10px 20px 10px; + background:#fff; +} + +#content a { + color:#184E81; +} + +#footer { + clear:both; + padding:10px; + text-align:center; + font-size:9pt; +} + +table { vertical-align:top; } + +table.list { + clear:both; + background:#ccc; + margin: 2px 0; + border-bottom: 1px solid #ccc; + font-family:arial, helvetica, sans-serif; + font-size:10pt; +} + +table.list caption { + text-align:left; + padding:5px; + background:#929292; + color:#fff; + font-weight:bold; +} + +table.list thead th { + background-color:#eee; + color:#000; + text-align:left; + vertical-align:top; +} + +table.list th a { + + text-decoration:none; + color:#000; +} + +table.list thead th a { padding: 3px; display: block; color: #000; background: url('../images/asc_desc.gif') 100% 50% no-repeat; } + +table.list thead th a.asc { background: url('../images/asc.gif') 100% 50% no-repeat #cfe6ff; } +table.list thead th a.desc { background: url('../images/desc.gif') 100% 50% no-repeat #cfe6ff; } +table.list tbody td { + background:#fff; + border:1px solid #fff; + padding:1px; + vertical-align:top; +} + +table.list tbody td { background: #fff; padding: 1px; padding-left:2px; vertical-align: top; } +table.list tbody tr.odd td { background-color: #f0faff; } +table.list tbody tr:hover td { background: #ffe; } +table.list tbody tr.odd:hover td { background: #ffd; } + +table.list tfoot td { + background:#eee; + padding: 2px; +} + +table.list tbody td.webticket, table.list tbody tr.row1 td.webticket { + text-indent:20px; + background:url(../images/icons/ticket_source_web.gif) 0 50% no-repeat #fff; +} + +table.list tbody td.emailticket, table.list tbody tr.row1 td.emailticket { + text-indent:20px; + background:url(../images/icons/ticket_source_email.gif) 0 50% no-repeat; +} + +table.list tbody td.phoneticket, table.list tbody tr.row1 td.phoneticket { + text-indent:20px; + background:url(../images/icons/ticket_source_phone.gif) 0 50% no-repeat; +} + +table.list tbody td.otherticket, table.list tbody tr.row1 td.otherticket { + text-indent:20px; + background:url(../images/icons/ticket_source_other.gif) 0 50% no-repeat; +} + +a.refresh { + display:block; + float:right; + width:auto; + height:16px; + line-height:16px; + padding:2px 5px 2px 2px; + background-position:2px 50%; + background-repeat:no-repeat; + padding-left:24px; + margin-left:10px; + margin-bottom: 2px; + border:1px solid #aaa; + background-image:url(../images/icons/refresh.gif); +} + +a.edit, a.print { + display:block; + float:right; + width:auto; + height:16px; + line-height:16px; + padding:2px 5px 2px 2px; + background-position:2px 50%; + background-repeat:no-repeat; + padding-left:24px; + margin-left:10px; + border:1px solid #aaa; + background-image:url(../images/icons/edit_ticket.png); +} + +a.print { + background-image:url(../images/icons/printer.gif); +} + +.btn { + padding:3px 10px; + background:url(../images/btn_bg.png) top left repeat-x #ccc; + border:1px solid #777; + color:#000; +} + +.button { padding:1px 5px; margin-right:10px; color:#777; font-weight:bold;} + +.btn_sm { + padding:2px 5px; + font-size:9pt; + background:url(../images/btn_sm_bg.png) top left repeat-x #f90; + border:1px solid #777; + color:#fff; + font-weight:bold; +} + +.btn:hover, .btn_sm:hover { + background-position: bottom left; +} + +.search label { + display:block; + line-height:25px; + height:25px; +} + +.search input[type=text] { + height:23px; + line-height:23px; + border:1px solid #aaa; + background:#fff; + padding:2px; +} + +.form_table { + margin-top:3px; + border-left:1px solid #ddd; + border-right:1px solid #ddd; +} + +.form_table td { + border-bottom:1px solid #ddd; +} + + +.form_table td.multi-line { + vertical-align:top; +} + +.form_table input[type=text], .form_table input[type=password], .form_table textarea { + background:#fff; + border:1px solid #aaa; +} + +.form_table input[type=radio], .form_table input[type=checkbox] { + position:relative; + top:3px; + margin-left:0; + padding-left:0; +} + +.form_table .required { + font-weight:bold; +} + +.form_table em { + font-weight:normal; + color:#666; +} + +.error { + color:#f00; +} + +.form_table .error input { + border:1px solid #f00; +} + +.form_table th { + text-align:left; + border:1px solid #ccc; + background:#eee; + padding:0; +} + +.form_table th h4 { + margin:0; + padding:5px; + color:#fff; + background:#929292; +} + +.form_table th em { + display:block; + padding:5px; + color:#000; +} + +.settings_table { + margin-top:2px; + border-left:1px solid #ddd; + border-right:1px solid #ddd; +} + +.settings_table td { + border-bottom:1px solid #ddd; +} + +.settings_table input[type=radio], .settings_table input[type=checkbox] { + margin-left:0; + padding-left:0; +} + +#content .settings_table th h4 a { + display:block; + color:#fff; +} + +.settings_table h4 a span { + font-size:12pt; + line-height:14px; + display:inline-block; + width:14px; + height:14px; + overflow:hidden; + text-align:center; + color:#444; + background:#ccc; + position:relative; + top:2px; +} + +h2 { + margin:0; + padding:0; + font-size:12pt; + color:#0A568E; +} + +h2 span { color:#000; } + +h3 { + margin:10px 0 0 0; + padding:5px 0; + font-size:10pt; + color:#444; +} + +.ticket_info th { + text-align:left; +} + +.ticket_info { + background:#F4FAFF; +} + +.right_align { text-align:right; } + +h2 .reload { + display:inline-block; + width:16px; + height:16px; + background:url(../images/icons/refresh.gif) top left no-repeat; + outline:none; + text-indent:-9999px; +} + +#assigned_message { + margin:10px 0; + padding:5px 5px 5px 30px; + background:url(../images/icons/assigned_ticket.gif) 5px 50% no-repeat #ffd; + border:1px solid #f90; +} + + + + +#ticket_actions { + padding:5px; + background:#eee; + border:1px solid #aaa; + border-bottom:none; + margin:0; +} + +#threads { + margin:0; + padding:5px 10px 0 10px; + border:1px solid #aaa; + background:#F4FAFF; + height:30px; +} + +#threads li { + list-style:none; + margin:0; + padding:0; + display:inline; +} + +#threads li a { + display:block; + width:auto; + float:left; + height:30px; + line-height:30px; + border-top:1px solid #F4FAFF; + padding:0 10px 0 32px; + margin-right:10px; +} + +#threads li a.active { + height:29px; + background-color:#fff; + border:1px solid #aaa; + border-bottom:none; + border-top:2px solid #ed9100; + font-weight:bold; +} + +#toggle_ticket_thread { + background:url(../images/icons/open.gif) 10px 50% no-repeat; +} + +#toggle_notes { + background:url(../images/icons/note.gif) 10px 50% no-repeat; +} + +#latest_notes { + margin:10px 0; + padding:10px; + background:#ffe; + border:1px solid #e7e765; +} + +#latest_notes h3 { + margin:0 0 10px 0; + padding:0; + font-size:11pt; +} + +#latest_notes h3 span, #latest_notes h3 a { + color:#777; + font-weight:normal; + text-decoration:none; + font-size:10pt; +} + +#latest_notes ul { + margin:0 20px; + padding:0; +} + +#latest_notes ul li { + margin:0; + padding:0 0 10px 0; + list-style:none; +} + +#latest_notes em { + color:#777; +} + +#ticket_thread table { + margin-top:10px; + border:1px solid #aaa; + border-bottom:2px solid #aaa; +} + +#ticket_notes table { + margin-top:10px; + border:1px solid #ddd; + border-bottom:2px solid #ddd; +} + +#ticket_thread table th, #ticket_notes table th { + text-align:left; + border-bottom:1px solid #aaa; + font-size:10pt; + padding:5px; +} + +#ticket_notes table th { + text-align:left; + border-bottom:1px solid #ddd; + font-size:10pt; + padding:5px; + background:#F4FAFF; +} + +#ticket_notes table th em { + font-weight:normal; + font-size:10pt; + color:#666; +} + +#ticket_thread .message th { + background:#C3D9FF; +} + +#ticket_notes .date { + font-weight:normal; + font-size:10pt; + color:#888; + text-align:right; +} + +#ticket_thread .response th { + background:#FFE0B3; +} + +#ticket_thread table td, #ticket_notes table td { + padding:5px; +} + +#ticket_notes td { + background:#f9f9f9; +} + +#ticket_thread .info, #ticket_notes .info { + padding:5px; + background:#F4FAFF; + height:16px; + line-height:16px; +} + +#ticket_notes .info { + background:#f9f9f9; +} + +#response_options { + margin-top:30px; +} + +#response_options form { + padding:0 10px; +} + +#response_options ul { + padding:4px 0 0 190px; + margin:0; + text-align:center; + height:29px; + border-bottom:1px solid #aaa; + background:#eef3f8; +} + +#response_options li { + margin:0; + padding:0; + display:inline; + list-style:none; +} + +#response_options li a { + width:130px; + font-weight:bold; + padding:5px; + height:18px; + line-height:20px; + color:#444; + display:block; + float:left; + outline:none; + position:relative; + top:0; + background:#fbfbfb; + border:1px solid #eee; + border-bottom:none; +} + +#response_options .reply_tab.tell { + color:#a00 !important; + background-image:url(../images/reminder.png); + background-position:12px 50%; + background-repeat:no-repeat; +} + +#response_options li a.active { + height:18px; + color:#184E81; + background-color:#f9f9f9; + border:1px solid #aaa; + border-top:2px solid #81a9d7; + border-bottom:none; +} + +#response_options form { + padding:10px 5px; + background:#f9f9f9; + border:1px solid #aaa; + border-top:none; +} + +#response_options table { + width:928px; +} + +#response_options td { + vertical-align:top; +} + +#response_options textarea { + width:760px !important; +} + +#response_options input[type=text], #response_options textarea { + border:1px solid #aaa; + background:#fff; +} + +.attachments .uploads div { + display:inline-block; + padding-right:20px; +} + + + +.file { + display:inline-block; + padding-left:20px; + margin-right:20px; + background:url(../images/icons/file.gif) 0 50% no-repeat; +} + +.expander { + line-height:14px; + display:inline-block; + width:12px; + height:12px; + overflow:hidden; + text-align:center; + color:#aaa; + position:relative; +} + +/** Popup Tool Tips and Content **/ + +.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; + -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; + width:auto !important; + width:300px; +} + +.tip_content hr { + + color: #ddd; + background-color: #ddd; + height: 1px; + border: 0; + padding: 0; + margin: 0.2em 0; + width: 100%; +} + +.tip_close { + position:absolute; + left:100%; + top:0; + margin-left:-12px; +} + +.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_menu { + margin:10px 0 0 0; + padding:5px 0; + border-top:1px solid #aaa; + height:16px; + font-size:9pt; +} + +.tip_menu li { + display:inline; + list-style:none; + margin:0; + padding:0; +} + +.tip_menu li a { + display:block; + width:auto; + _width:0; + float:left; + padding:0 10px; + border-right:1px solid #ddd; + color:#666; +} + +.tip_menu li a:hover { + color:#E76C74; +} + +.tip_content form { + display:none; + line-height:24px; +} + +.tip_content select, .tip_content textarea { + width:295px; +} + +.tip_content textarea { + padding:0; + border:1px solid #aaa; + background:#fff; +} + +.tip_content form p { + margin:0; + width:auto !important; + width:295px; + text-align:right; + line-height:24px; +} + +/* Knowledgebase */ +/* Knowledgebase */ +#kb { + margin: 2px 0; + padding: 5px; + overflow: hidden; +} + +#kb li { + padding:10px 10px 10px 46px; + height:auto !important; + overflow:hidden; + margin:0; + background-image:url(../images/kb_large_folder.png), url(../images/kb_category_bg.png); + background-position:0 50%, bottom left; + background-repeat:no-repeat, repeat-x; + border-bottom:1px solid #ddd; +} + + +#kb li h4 { + padding-bottom:3px; + margin:0 0 3px 0; +} + +#kb li h4 span { + color:#666; + font-weight:normal; +} + +#kb li h4 a { + font-size: 14px; +} + +#kbSearch { + padding:10px 0; + overflow:hidden; +} + +#kbSearch div { + clear:both; + overflow:hidden; + padding-top:5px; +} + +#kbSearch #query { + margin:0; + display:inline-block; + float:left; + width:200px; + margin-right:5px; +} + +#kbSearch #cid { + margin:0; + display:inline-block; + float:left; + width:200px; + margin-right:5px; + position:relative; + top:2px; +} + +#kbSearch #topic-id { + margin:0; + display:inline-block; + float:left; + width:410px; +} + +#kbSearch #searchSubmit { + margin:0; + display:inline-block; + float:left; + position:relative; + top:2px; +} + +#faq { + clear: both; + margin: 0; + padding: 5 0 10px 5px; +} +#faq ol { + font-size: 15px; + margin-left: 0; + padding-left: 0; +} +#faq ol li { + list-style: none; + margin: 0; + padding:5px 0; + color: #999; + border-bottom:1px solid #ddd; +} + +#faq ol li a { + display: inline; + height: 16px; + font-size:13px; + line-height: 16px; + padding-left: 24px; + background: url('../images/icons/page.png') 0 50% no-repeat; +} + +#faq ol li a span { + font-weight:normal; + color:#777; +} + +#faq ol li:hover { + background-color:#e9f5ff; +} + +time { + display:inline-block; + float:right; + color:#777; +} + +.cat-desc { + padding-top:5px; + padding-bottom:25px; +} + +.cat-manage-bar { + background:#e3f5ff; + padding:5px; + border-bottom:1px solid #777; +} + +.cat-manage-bar a { + display:inline-block; + margin-right:20px; +} + +/* Advanced Ticket Search */ + +#overlay { + background:#000; + position:absolute; + display:none; + z-index:1000; +} + +#advanced-search, #advanced-search * { + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +#advanced-search { + position:absolute; + padding:1em; + width:640px; + height:360px; + background:#fff; + border:1px solid #2a67ac; + display:none; + z-index:1200; +} + +#advanced-search h3 { + color:#2a67ac; + font-size:20px; + margin:0; + padding:0; + display:inline-block; +} + +#advanced-search a.close { + display:inline-block; + float:right; + font-size:16px; + color:#777; +} + +#advanced-search form { + clear:both; + padding:2em 0 1em 0; + width:100%; +} + +#advanced-search div.closed_by, #advanced-search span.spinner { + display:none; +} + +#advanced-search fieldset { + margin:0; + padding:0.25em 0; + border:none; + overflow:hidden; +} + +#advanced-search label { + width:100px; + display:inline-block; + text-align:right; + padding:10px; +} + +#advanced-search fieldset input { + border:1px solid #ccc; + background:#fff; +} + +#advanced-search fieldset select { + width:170px; + display:inline-block; +} + +#advanced-search fieldset span { + width:50px; + display:inline-block; + text-align:center; + color:#777; + font-size:0.75em; +} + +#advanced-search .query input { + width:350px; +} + +#advanced-search .date_range input { + width:175px; +} + +#advanced-search .date_range i { + display:inline-block; + margin-left:3px; + position:relative; + top:5px; + width:16px; + height:16px; + background:url(../images/cal.png) bottom left no-repeat; +} + +#advanced-search fieldset.sorting select { + width:130px; +} + +#advanced-search p { + text-align:center; +} + +#advanced-search input[type="submit"], +#advanced-search input[type="reset"], +#advanced-search input[type="button"] +{ + display:inline-block; + margin:0; + height:24px; + line-height:24px; + font-weight:bold; + border:1px solid #666666; + padding:0 10px; + background: url('../images/grey_btn_bg.png?1312910883') top left repeat-x; + color: #333; +} + +#advanced-search input[type="reset"], #advanced-search input[type="button"] { + opacity:0.7; +} + +#advanced-search input[type=submit]:hover, #advanced-search input[type=submit]:active, +#advanced-search input[type=reset]:hover, #advanced-search input[type=reset]:active { + background-position:bottom left; +} + +#result-count div { + padding:5px 10px; + text-align:left; + font-weight:bold; + width:100%; + margin:0 auto; +} + +#result-count .success { + background:#e3ffd8; + border:1px solid #0a0; +} + +#result-count .fail { + background:#ffd8d8; + border:1px solid #a00; +} diff --git a/scp/images/ajax-loader.gif b/scp/images/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..d42f72c723644bbf8cf8d6e1b7ff0bea7ddd305a Binary files /dev/null and b/scp/images/ajax-loader.gif differ diff --git a/scp/images/icons/canned.gif b/scp/images/icons/canned.gif new file mode 100644 index 0000000000000000000000000000000000000000..8539a783ddfb80608aad5107785e3e67aa791c57 Binary files /dev/null and b/scp/images/icons/canned.gif differ diff --git a/scp/images/icons/delete_category.png b/scp/images/icons/delete_category.png new file mode 100644 index 0000000000000000000000000000000000000000..7afa4a1a80afe76791fad19ecdb62b7dcb8fb47f Binary files /dev/null and b/scp/images/icons/delete_category.png differ diff --git a/scp/images/icons/edit_category.png b/scp/images/icons/edit_category.png new file mode 100644 index 0000000000000000000000000000000000000000..cce44c7ff6efee4891f7cb6a99936b1a8447eeb3 Binary files /dev/null and b/scp/images/icons/edit_category.png differ diff --git a/scp/images/icons/kb-categories.gif b/scp/images/icons/kb-categories.gif new file mode 100644 index 0000000000000000000000000000000000000000..4a0293cfd78eb8795a05301ad88ec87e56c5da61 Binary files /dev/null and b/scp/images/icons/kb-categories.gif differ diff --git a/scp/images/icons/kb-categories.png b/scp/images/icons/kb-categories.png new file mode 100644 index 0000000000000000000000000000000000000000..2a175ed4eea1dc8fa682a46254db4bfb7992f535 Binary files /dev/null and b/scp/images/icons/kb-categories.png differ diff --git a/scp/images/icons/kb.gif b/scp/images/icons/kb.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe969df4e22a5dfe437757e096b4b251d5684360 Binary files /dev/null and b/scp/images/icons/kb.gif differ diff --git a/scp/images/icons/new_category.png b/scp/images/icons/new_category.png new file mode 100644 index 0000000000000000000000000000000000000000..ef036c2565f43c132824f9fb8d89128d70918290 Binary files /dev/null and b/scp/images/icons/new_category.png differ diff --git a/scp/images/icons/new_faq.png b/scp/images/icons/new_faq.png new file mode 100644 index 0000000000000000000000000000000000000000..49bb3baecc76753b9b76e50190844ef6223dd861 Binary files /dev/null and b/scp/images/icons/new_faq.png differ diff --git a/scp/images/icons/new_reply.png b/scp/images/icons/new_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..b09dc40666dab28f988f1a261c696c8a006211b2 Binary files /dev/null and b/scp/images/icons/new_reply.png differ diff --git a/scp/images/kb_category_bg.png b/scp/images/kb_category_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..174d675f4c491060a413232bdef7e0e9298df60e Binary files /dev/null and b/scp/images/kb_category_bg.png differ diff --git a/scp/images/kb_large_folder.png b/scp/images/kb_large_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..144fa828a3fc83613dd7a5b29368b6da290fd09d Binary files /dev/null and b/scp/images/kb_large_folder.png differ diff --git a/scp/js/scp.js b/scp/js/scp.js index 7a9e0e218d1da97b3ec4b2860f307dd1c1769d1f..56f776ed6ff4ef0a6a20a54ef3837d957a487949 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -198,11 +198,11 @@ $(document).ready(function(){ } } - /* Typeahead init */ + /* Typeahead tickets lookup */ $('#basic-ticket-search').typeahead({ source: function (typeahead, query) { $.ajax({ - url: "ajax.php/tickets?q="+query, + url: "ajax.php/tickets/lookup?q="+query, dataType: 'json', success: function (data) { typeahead.process(data); @@ -215,6 +215,7 @@ $(document).ready(function(){ property: "value" }); + /* Typeahead user lookup */ $('#email.typeahead').typeahead({ source: function (typeahead, query) { if(query.length > 2) { @@ -235,5 +236,84 @@ $(document).ready(function(){ property: "email" }); + /* advanced search */ + $("#overlay").css({ + opacity : 0.3, + top : 0, + left : 0, + width : $(window).width(), + height : $(window).height() + }); + + $("#advanced-search").css({ + top : ($(window).height() / 6), + left : ($(window).width() / 2 - 300) + }); + + $('#go-advanced').click(function(e) { + e.preventDefault(); + $('#result-count').html(''); + $('#overlay').show(); + $('#advanced-search').show(); + }); + + $('#advanced-search').delegate('a.close, input.close', 'click', function(e) { + e.preventDefault(); + $('#advanced-search').hide() + $('#overlay').hide(); + }).delegate('#status', 'change', function() { + switch($(this).val()) { + case 'closed': + $('select#assignee').find('option:first').attr('selected', 'selected').parent('select'); + $('select#assignee').attr('disabled','disabled'); + $('select#staffId').removeAttr('disabled'); + break; + case 'open': + case 'overdue': + $('select#staffId').find('option:first').attr('selected', 'selected').parent('select'); + $('select#staffId').attr('disabled','disabled'); + $('select#assignee').removeAttr('disabled'); + break; + default: + $('select#staffId').removeAttr('disabled'); + $('select#assignee').removeAttr('disabled'); + } + }); + + $('#advanced-search form#search').submit(function(e) { + e.preventDefault(); + var fObj = $(this); + var elem = $('#advanced-search'); + $('#result-count').html(''); + $.ajax({ + url: "ajax.php/tickets", + data: fObj.serialize(), + dataType: 'json', + beforeSend: function ( xhr ) { + $('.buttons', elem).hide(); + $('.spinner', elem).show(); + return true; + }, + success: function (resp) { + + if(resp.success) { + $('#result-count').html('<div class="success">' + resp.success +'</div>'); + } else if (resp.fail) { + $('#result-count').html('<div class="fail">' + resp.fail +'</div>'); + } else { + $('#result-count').html('<div class="fail">Unknown error</div>'); + } + } + }) + .done( function () { + }) + .fail( function () { + $('#result-count').html('<div class="fail">Advanced search failed - try again!</div>'); + }) + .always( function () { + $('.spinner', elem).hide(); + $('.buttons', elem).show(); + }); + }); }); diff --git a/scp/staff.inc.php b/scp/staff.inc.php index b3ee30a4ec3a555c2fd8c3c86ed6e72b2c6517ca..7a4dfb9e3a596fcc480894c60ecf2bede2304181 100644 --- a/scp/staff.inc.php +++ b/scp/staff.inc.php @@ -63,19 +63,19 @@ if(!$thisstaff || !is_object($thisstaff) || !$thisstaff->getId() || !$thisstaff- exit; } //2) if not super admin..check system status and group status -if(!$thisstaff->isadmin()){ - //Staff are not allowed to login in offline mode!! - if($cfg->isHelpDeskOffline()){ - staffLoginPage('System Offline'); - exit; - } +if(!$thisstaff->isAdmin()) { //Check for disabled staff or group! if(!$thisstaff->isactive() || !$thisstaff->isGroupActive()) { staffLoginPage('Access Denied. Contact Admin'); exit; } -} + //Staff are not allowed to login in offline mode!! + if($cfg->isHelpDeskOffline() || $cfg->isUpgradePending()) { + staffLoginPage('System Offline'); + exit; + } +} //Keep the session activity alive $thisstaff->refreshSession(); @@ -93,10 +93,9 @@ $errors=array(); $msg=$warn=$sysnotice=''; $tabs=array(); $submenu=array(); - -if(defined('THIS_VERSION') && strcasecmp($cfg->getVersion(),THIS_VERSION)) { - $errors['err']=$sysnotice=sprintf('The script is version %s while the database is version %s',THIS_VERSION,$cfg->getVersion()); -}elseif($cfg->isHelpDeskOffline()){ +if($cfg->isUpgradePending()) { + $errors['err']=$sysnotice='System upgrade is pending <a href="../setup/upgrade.php">Upgrade Now</a>'; +} elseif($cfg->isHelpDeskOffline()) { $sysnotice='<strong>System is set to offline mode</strong> - Client interface is disabled and ONLY admins can access staff control panel.'; $sysnotice.=' <a href="settings.php">Enable</a>.'; } diff --git a/scp/tickets.php b/scp/tickets.php index c881381e0d9ff9613b1e26e1df6c410157df664b..567c3f69755525aca1146ce8433a44e2485c6fc3 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -55,7 +55,7 @@ if($_POST && !$errors): if(!$errors['err'] && EmailFilter::isBanned($ticket->getEmail())) $errors['err']='Email is in banlist. Must be removed to reply.'; - $wasOpen =($ticket->isopen()); + $wasOpen =($ticket->isOpen()); //If no error...do the do. if(!$errors && ($respId=$ticket->postReply($_POST,$_FILES['attachments'],$errors))) { $msg='Reply posted successfully'; @@ -189,7 +189,7 @@ if($_POST && !$errors): } break; case 'close': - if(!$thisstaff->isadmin() && !$thisstaff->canCloseTickets()){ + if(!$thisstaff->isAdmin() && !$thisstaff->canCloseTickets()){ $errors['err']='Perm. Denied. You are not allowed to close tickets.'; }else{ if($ticket->close()){ @@ -204,7 +204,7 @@ if($_POST && !$errors): break; case 'reopen': //if they can close...then assume they can reopen. - if(!$thisstaff->isadmin() && !$thisstaff->canCloseTickets()){ + if(!$thisstaff->isAdmin() && !$thisstaff->canCloseTickets()){ $errors['err']='Perm. Denied. You are not allowed to reopen tickets.'; }else{ if($ticket->reopen()){ @@ -233,7 +233,7 @@ if($_POST && !$errors): break; case 'overdue': //Mark the ticket as overdue - if(!$thisstaff->isadmin() && !$thisstaff->isManager()){ + if(!$thisstaff->isAdmin() && !$thisstaff->isManager()){ $errors['err']='Perm. Denied. You are not allowed to flag tickets overdue'; }else{ if($ticket->markOverdue()){ @@ -252,7 +252,7 @@ if($_POST && !$errors): } break; case 'banemail': - if(!$thisstaff->isadmin() && !$thisstaff->canManageBanList()){ + if(!$thisstaff->isAdmin() && !$thisstaff->canBanEmails()){ $errors['err']='Perm. Denied. You are not allowed to ban emails'; }elseif(Banlist::add($ticket->getEmail(),$thisstaff->getName())){ $msg='Email ('.$ticket->getEmail().') added to banlist'; @@ -266,7 +266,7 @@ if($_POST && !$errors): } break; case 'unbanemail': - if(!$thisstaff->isadmin() && !$thisstaff->canManageBanList()){ + if(!$thisstaff->isAdmin() && !$thisstaff->canBanEmails()){ $errors['err']='Perm. Denied. You are not allowed to remove emails from banlist.'; }elseif(Banlist::remove($ticket->getEmail())){ $msg='Email removed from banlist'; @@ -275,7 +275,7 @@ if($_POST && !$errors): } break; case 'delete': // Dude what are you trying to hide? bad customer support?? - if(!$thisstaff->isadmin() && !$thisstaff->canDeleteTickets()){ + if(!$thisstaff->isAdmin() && !$thisstaff->canDeleteTickets()){ $errors['err']='Perm. Denied. You are not allowed to DELETE tickets!!'; }else{ if($ticket->delete()){ @@ -339,7 +339,7 @@ if($_POST && !$errors): $note='Ticket flagged as overdue by '.$thisstaff->getName(); foreach($_POST['tids'] as $k=>$v) { $t = new Ticket($v); - if($t && !$t->isoverdue()) + if($t && !$t->isOverdue()) if($t->markOverdue()) { $i++; $t->logActivity('Ticket Marked Overdue',$note,false,'System'); diff --git a/setup/cleanup.php b/setup/cleanup.php deleted file mode 100644 index 1309228eb6c64eaaee6f16926ec98b06d91d0221..0000000000000000000000000000000000000000 --- a/setup/cleanup.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/********************************************************************* - cleanup.php - - Cleanup script called via ajax to migrate attachments. - - Peter Rotich <peter@osticket.com> - Copyright (c) 2006-2012 osTicket - http://www.osticket.com - - Released under the GNU General Public License WITHOUT ANY WARRANTY. - See LICENSE.TXT for details. - - vim: expandtab sw=4 ts=4 sts=4: -**********************************************************************/ -session_start(); -if($_GET['c']>10) { //When Done send 304 - nothing else to do. - $_SESSION['s']='done'; - session_write_close(); - header("HTTP/1.1 304 Not Modified"); - exit; -} -echo "Cleaning up...".time(); -?> diff --git a/setup/css/wizard.css b/setup/css/wizard.css index 6fd18e17854f65a30a2947460a955ca4c403541c..c92b05447a6acb9992ea21be3ce13581840784a9 100644 --- a/setup/css/wizard.css +++ b/setup/css/wizard.css @@ -70,7 +70,7 @@ form .row span { width: 600px; color: #666666; } #overlay { display: none; position: fixed; background: #000; z-index: 2000; } -#loading { padding: 10px 10px 10px 60px; width: 300px; height: 100px; background: url('../images/ajax-loader.gif?1312925608') 10px 50% no-repeat white; position: fixed; display: none; z-index: 3000; } +#loading { padding: 10px 10px 10px 60px; width: 400px; height: 150px; background: url('../images/ajax-loader.gif?1312925608') 10px 50% no-repeat white; position: fixed; display: none; z-index: 3000; } #loading h4 { margin: 3px 0 0 0; padding: 0; color: #d80; } .tip { display: inline-block; width: 16px; height: 16px; outline: none; text-decoration: none; color: #d80; } diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php new file mode 100644 index 0000000000000000000000000000000000000000..7b3dbe534eb0af30f13f27223f555849d30a6bb1 --- /dev/null +++ b/setup/inc/class.installer.php @@ -0,0 +1,204 @@ +<?php +/********************************************************************* + class.installer.php + + osTicket Intaller - installs the latest version. + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2012 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +require_once INC_DIR.'class.setup.php'; + +class Installer extends SetupWizard { + + var $config; + + function Installer($configfile) { + $this->config =$configfile; + $this->errors=array(); + } + + function getConfigFile() { + return $this->config; + } + + function config_exists() { + return ($this->getConfigFile() && file_exists($this->getConfigFile())); + } + + function config_writable() { + return ($this->getConfigFile() && is_writable($this->getConfigFile())); + } + + function check_config() { + return ($this->config_exists() && $this->config_writable()); + } + + //XXX: Latest version insall logic...no carry over. + function install($vars) { + + $this->errors=$f=array(); + + $f['name'] = array('type'=>'string', 'required'=>1, 'error'=>'Name required'); + $f['email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); + $f['fname'] = array('type'=>'string', 'required'=>1, 'error'=>'First name required'); + $f['lname'] = array('type'=>'string', 'required'=>1, 'error'=>'Last name required'); + $f['admin_email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); + $f['username'] = array('type'=>'username', 'required'=>1, 'error'=>'Username required'); + $f['passwd'] = array('type'=>'password', 'required'=>1, 'error'=>'Password required'); + $f['passwd2'] = array('type'=>'string', 'required'=>1, 'error'=>'Confirm password'); + $f['prefix'] = array('type'=>'string', 'required'=>1, 'error'=>'Table prefix required'); + $f['dbhost'] = array('type'=>'string', 'required'=>1, 'error'=>'Hostname required'); + $f['dbname'] = array('type'=>'string', 'required'=>1, 'error'=>'Database name required'); + $f['dbuser'] = array('type'=>'string', 'required'=>1, 'error'=>'Username required'); + $f['dbpass'] = array('type'=>'string', 'required'=>1, 'error'=>'password required'); + + + if(!Validator::process($f,$vars,$this->errors) && !$this->errors['err']) + $this->errors['err']='Missing or invalid data - correct the errors and try again.'; + + + //Staff's email can't be same as system emails. + if($vars['admin_email'] && $vars['email'] && !strcasecmp($vars['admin_email'],$vars['email'])) + $this->errors['admin_email']='Conflicts with system email above'; + //Admin's pass confirmation. + if(!$this->errors && strcasecmp($vars['passwd'],$vars['passwd2'])) + $this->errors['passwd2']='passwords to not match!'; + //Check table prefix underscore required at the end! + if($vars['prefix'] && substr($vars['prefix'], -1)!='_') + $this->errors['prefix']='Bad prefix. Must have underscore (_) at the end. e.g \'ost_\''; + + //Make sure admin username is not very predictable. XXX: feels dirty but necessary + if(!$this->errors['username'] && in_array(strtolower($vars['username']),array('admin','admins','username','osticket'))) + $this->errors['username']='Bad username'; + + //MYSQL: Connect to the DB and check the version & database (create database if it doesn't exist!) + if(!$this->errors) { + if(!db_connect($vars['dbhost'],$vars['dbuser'],$vars['dbpass'])) + $this->errors['db']='Unable to connect to MySQL server. Possibly invalid login info.'; + elseif(db_version()< $this->getMySQLVersion()) + $this->errors['db']=sprintf('osTicket requires MySQL %s or better!',$this->getMySQLVersion()); + elseif(!db_select_database($vars['dbname']) && !db_create_database($vars['dbname'])) { + $this->errors['dbname']='Database doesn\'t exist'; + $this->errors['db']='Unable to create the database.'; + } elseif(!db_select_database($vars['dbname'])) { + $this->errors['dbname']='Unable to select the database'; + } + } + + //bailout on errors. + if($this->errors) return false; + + /*************** We're ready to install ************************/ + define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install. + define('PREFIX',$vars['prefix']); //Table prefix + + $schemaFile =INC_DIR.'sql/osticket-v1.7-mysql.sql'; //DB dump. + $debug = true; //XXX:Change it to true to show SQL errors. + + //Last minute checks. + if(!file_exists($schemaFile)) + $this->errors['err']='Internal Error - please make sure your download is the latest (#1)'; + elseif(!($signature=trim(file_get_contents("$schemaFile.md5"))) || strcasecmp($signature, md5_file($schemaFile))) + $this->errors['err']='Unknown or invalid schema signature ('.$signature.' .. '.md5_file($schemaFile).')'; + elseif(!file_exists($this->getConfigFile()) || !($configFile=file_get_contents($this->getConfigFile()))) + $this->errors['err']='Unable to read config file. Permission denied! (#2)'; + elseif(!($fp = @fopen($this->getConfigFile(),'r+'))) + $this->errors['err']='Unable to open config file for writing. Permission denied! (#3)'; + elseif(!$this->load_sql_file($schemaFile,$vars['prefix'], true, $debug)) + $this->errors['err']='Error parsing SQL schema! Get help from developers (#4)'; + + if(!$this->errors) { + //Create admin user. + $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() ' + .', isactive=1, isadmin=1, group_id=1, dept_id=1, timezone_id=8, max_page_size=25 ' + .', email='.db_input($_POST['admin_email']) + .', firstname='.db_input($vars['fname']) + .', lastname='.db_input($vars['lname']) + .', username='.db_input($vars['username']) + .', passwd='.db_input(Passwd::hash($vars['passwd'])); + if(!mysql_query($sql) || !($uid=mysql_insert_id())) + $this->errors['err']='Unable to create admin user (#6)'; + } + + if(!$this->errors) { + //Create config settings---default settings! + //XXX: rename ostversion helpdesk_* ?? + $sql='INSERT INTO '.PREFIX.'config SET updated=NOW(), isonline=0 ' + .', default_email_id=1, alert_email_id=2, default_dept_id=1 ' + .', default_sla_id=1, default_timezone_id=8, default_template_id=1 ' + .', admin_email='.db_input($vars['admin_email']) + .', schema_signature='.db_input($signature) + .', helpdesk_url='.db_input(URL) + .', helpdesk_title='.db_input($vars['name']); + if(!mysql_query($sql) || !($cid=mysql_insert_id())) + $this->errors['err']='Unable to create config settings (#7)'; + } + + if($this->errors) return false; //Abort on internal errors. + + + //Rewrite the config file - MUST be done last to allow for installer recovery. + $configFile= str_replace("define('OSTINSTALLED',FALSE);","define('OSTINSTALLED',TRUE);",$configFile); + $configFile= str_replace('%ADMIN-EMAIL',$vars['admin_email'],$configFile); + $configFile= str_replace('%CONFIG-DBHOST',$vars['dbhost'],$configFile); + $configFile= str_replace('%CONFIG-DBNAME',$vars['dbname'],$configFile); + $configFile= str_replace('%CONFIG-DBUSER',$vars['dbuser'],$configFile); + $configFile= str_replace('%CONFIG-DBPASS',$vars['dbpass'],$configFile); + $configFile= str_replace('%CONFIG-PREFIX',$vars['prefix'],$configFile); + $configFile= str_replace('%CONFIG-SIRI',Misc::randcode(32),$configFile); + if(!$fp || !ftruncate($fp,0) || !fwrite($fp,$configFile)) { + $this->errors['err']='Unable to write to config file. Permission denied! (#5)'; + return false; + } + @fclose($fp); + + /************* Make the system happy ***********************/ + //Create default emails! + $email = $vars['email']; + list(,$domain)=explode('@',$vars['email']); + $sql='INSERT INTO '.PREFIX.'email (`email_id`, `dept_id`, `name`,`email`,`created`,`updated`) VALUES ' + ." (1,1,'Support','$email',NOW(),NOW())" + .",(2,1,'osTicket Alerts','alerts@$domain',NOW(),NOW())" + .",(3,1,'','noreply@$domain',NOW(),NOW())"; + @mysql_query($sql); + + //Create a ticket to make the system warm and happy. + $sql='INSERT INTO '.PREFIX.'ticket SET created=NOW(), status="open", source="Web" ' + .' ,priority_id=2, dept_id=1, topic_id=1 ' + .' ,ticketID='.db_input(Misc::randNumber(6)) + .' ,email="support@osticket.com" ' + .' ,name="osTicket Support" ' + .' ,subject="osTicket Installed!"'; + if(mysql_query($sql) && ($tid=mysql_insert_id())) { + if(!($msg=file_get_contents(INC_DIR.'msg/installed.txt'))) + $msg='Congratulations and Thank you for choosing osTicket!'; + + $sql='INSERT INTO '.PREFIX.'ticket_thread SET created=NOW()' + .', source="Web" ' + .', thread_type="M" ' + .', ticket_id='.db_input($tid) + .', title='.db_input('osTicket Installed') + .', body='.db_input($msg); + @mysql_query($sql); + } + //TODO: create another personalized ticket and assign to admin?? + + //Log a message. + $msg="Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!"; + $sql='INSERT INTO '.PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" ' + .', title="osTicket installed!"' + .', log='.db_input($msg) + .', ip_address='.db_input($_SERVER['REMOTE_ADDR']); + @mysql_query($sql); + + return true; + } +} +?> diff --git a/setup/inc/class.migrater.php b/setup/inc/class.migrater.php new file mode 100644 index 0000000000000000000000000000000000000000..de8a3d9590e97b9d78ecb905528bcbc1bc2c03a5 --- /dev/null +++ b/setup/inc/class.migrater.php @@ -0,0 +1,67 @@ +<?php +/********************************************************************* + class.migrater.php + + SQL database migrater. This provides the engine capable of rolling the + database for an osTicket installation forward (and perhaps even + backward) in time using a set of included migration scripts. Each script + will roll the database between two database checkpoints. Where possible, + the migrater will roll several checkpoint scripts into one to be applied + together. + + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2012 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +class DatabaseMigrater { + + var $start; + var $end; + var $sqldir; + + function DatabaseMigrater($start, $end, $sqldir) { + + $this->start = $start; + $this->end = $end; + $this->sqldir = $sqldir; + + } + + function getPatches($stop=null) { + + $start= $this->start; + $stop = $stop?$stop:$this->end; + + $patches = array(); + while (true) { + $next = glob($this->sqldir . substr($start, 0, 8) + . '-*.patch.sql'); + if (count($next) == 1) { + $patches[] = $next[0]; + $start = substr(basename($next[0]), 9, 8); + } elseif (count($next) == 0) { + # There are no patches leaving the current signature. We + # have to assume that we've applied all the available + # patches. + break; + } else { + # Problem -- more than one patch exists from this snapshot. + # We probably need a graph approach to solve this. + break; + } + + # Break if we've reached our target stop. + if(!$start || !strncasecmp($start, $stop, 8)) + break; + } + + return $patches; + } +} +?> diff --git a/setup/inc/class.setup.php b/setup/inc/class.setup.php index c6bf7b6751c9e80236d3cbfe143a37c6d52cf6c5..438eca54505302a30d6da9575067c90b296369dc 100644 --- a/setup/inc/class.setup.php +++ b/setup/inc/class.setup.php @@ -21,8 +21,8 @@ Class SetupWizard { 'mysql' => '4.4'); //Version info - same as the latest version. - var $version ='1.7-rc1'; - var $version_verbose='1.7 RC 1'; + var $version ='1.7-dpr3'; + var $version_verbose='1.7 DPR 3'; //Errors var $errors=array(); @@ -31,34 +31,34 @@ Class SetupWizard { $this->errors=array(); } - function load_sql_file($file, $prefix, $debug=false) { + function load_sql_file($file, $prefix, $abort=true, $debug=false) { if(!file_exists($file) || !($schema=file_get_contents($file))) - return $this->abort('Error accessing SQL file'); + return $this->abort('Error accessing SQL file '.basename($file), $debug); - return $this->load_sql($schema, $prefix, $debug); + return $this->load_sql($schema, $prefix, $abort, $debug); } /* load SQL schema - assumes MySQL && existing connection */ - function load_sql($schema, $prefix, $debug=false) { + function load_sql($schema, $prefix, $abort=true, $debug=false) { # Strip comments and remarks - $schema=preg_replace('%^\s*(#|--).*$%m','',$schema); + $schema=preg_replace('%^\s*(#|--).*$%m', '', $schema); # Replace table prefis - $schema = str_replace('%TABLE_PREFIX%',$prefix, $schema); + $schema = str_replace('%TABLE_PREFIX%', $prefix, $schema); # Split by semicolons - and cleanup if(!($statements = array_filter(array_map('trim', @explode(';', $schema))))) - return $this->abort('Error parsing SQL schema'); + return $this->abort('Error parsing SQL schema', $debug); @mysql_query('SET SESSION SQL_MODE =""'); foreach($statements as $k=>$sql) { - if(!mysql_query($sql)) { - if($debug) echo "[$sql]=>".mysql_error(); - return $this->abort("[$sql] - ".mysql_error()); - } + if(mysql_query($sql)) continue; + $error = "[$sql] ".mysql_error(); + if($abort) + return $this->abort($error, $debug); } return true; @@ -81,7 +81,7 @@ Class SetupWizard { } function check_php() { - return (version_compare(PHP_VERSION,$this->getPHPVersion())>=0); + return (version_compare(PHP_VERSION, $this->getPHPVersion())>=0); } function check_mysql() { @@ -95,293 +95,28 @@ Class SetupWizard { /* @error is a mixed var. */ - function abort($error) { - + function abort($error, $debug=false) { + + if($debug) echo $error; + $this->onError($error); + return false; // Always false... It's an abort. + } + + function setError($error) { + if($error && is_array($error)) - $this->errors = array_merge($this->errors,$error); + $this->errors = array_merge($this->errors, $error); elseif($error) $this->errors[] = $error; - - //Always returns FALSE. - return false; } function getErrors(){ - return $this->errors; } - /* Access and user validation*/ - - function getThisUser() { - - - } -} - -class Upgrader extends SetupWizard { - - var $prefix; - - function Upgrader($prefix) { - $this->prefix = $prefix; - $this->errors = array(); - } - - function getTablePrefix() { - return $this->prefix; - } - - /* upgrade magic related to the given version */ - function upgradeTo($version) { - - $errors = array(); - switch($version) { - case '1.7-RC1': - //TODO: latest upgrade logic. - break; - case '1.6 ST': - //TODO: refactor code from 1.6 ST. - break; - case '1.6 RC5': - //TODO: refactor code from 1.6 ST. - break; - default: - //XXX: escape version - return $this->abort('Trying to upgrade unknown version '.$version); - } - - if($errors) - return $this->abort($errors); - - return true; - } - - /* - Do base upgrade - Does fall-through upgrade until we reach the current version. - We're assumming the user - is upgrading upgradable version of osTicket! - @version - version number to upgrade from! - */ - function upgradeFrom($version) { - - if(!$version || $this->getErrors()) - return false; - - if(!strcasecmp($version,$this->getVersion())) - return true; - - //XXX: Note FALLTHROUGH (we only break on error) and uppercase cases. - switch(strtoupper($version)) { - case 'OLD': //Upgrade old versions to 1.6 ST. - if(!$this->upgradeTo('1.6 RC5')) break; - /* FALLTHROUGH */ - case '1.6 RC5': //Upgrade 1.6 RC5 to 1.6 ST - if(!$this->upgradeTo('1.6 ST')) break; - /* FALLTHROUGH */ - case '1.6 ST': //Upgrade 1.6 ST to to 1.7 RC1 - if(!$this->upgradeTo('1.7-RC1')) break; - /* LAST CASE IS NOT FALLTHROUGH */ - break; - default: //Catch all - Upgrading older versions 1.3+ - return $this->upgradeFrom('OLD'); - } - //XXX: Set errors??? - - return (!$this->getErrors()); - } - - function cleanup() { - //FIXME: cleanup logic here. - sleep(2); - - return true; - } -} - -/* - Installer class - latest version. - */ -class Installer extends SetupWizard { - - var $config; - - function Installer($configfile) { - $this->config =$configfile; - $this->errors=array(); - } - - function getConfigFile() { - return $this->config; - } - - function config_exists() { - return ($this->getConfigFile() && file_exists($this->getConfigFile())); - } - - function config_writable() { - return ($this->getConfigFile() && is_writable($this->getConfigFile())); - } - - function check_config() { - return ($this->config_exists() && $this->config_writable()); - } - - //XXX: Latest version insall logic...no carry over. - function install($vars) { - - $this->errors=$f=array(); - - $f['name'] = array('type'=>'string', 'required'=>1, 'error'=>'Name required'); - $f['email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); - $f['fname'] = array('type'=>'string', 'required'=>1, 'error'=>'First name required'); - $f['lname'] = array('type'=>'string', 'required'=>1, 'error'=>'Last name required'); - $f['admin_email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); - $f['username'] = array('type'=>'username', 'required'=>1, 'error'=>'Username required'); - $f['passwd'] = array('type'=>'password', 'required'=>1, 'error'=>'Password required'); - $f['passwd2'] = array('type'=>'string', 'required'=>1, 'error'=>'Confirm password'); - $f['prefix'] = array('type'=>'string', 'required'=>1, 'error'=>'Table prefix required'); - $f['dbhost'] = array('type'=>'string', 'required'=>1, 'error'=>'Hostname required'); - $f['dbname'] = array('type'=>'string', 'required'=>1, 'error'=>'Database name required'); - $f['dbuser'] = array('type'=>'string', 'required'=>1, 'error'=>'Username required'); - $f['dbpass'] = array('type'=>'string', 'required'=>1, 'error'=>'password required'); - - - if(!Validator::process($f,$vars,$this->errors) && !$this->errors['err']) - $this->errors['err']='Missing or invalid data - correct the errors and try again.'; - - - //Staff's email can't be same as system emails. - if($vars['admin_email'] && $vars['email'] && !strcasecmp($vars['admin_email'],$vars['email'])) - $this->errors['admin_email']='Conflicts with system email above'; - //Admin's pass confirmation. - if(!$this->errors && strcasecmp($vars['passwd'],$vars['passwd2'])) - $this->errors['passwd2']='passwords to not match!'; - //Check table prefix underscore required at the end! - if($vars['prefix'] && substr($vars['prefix'], -1)!='_') - $this->errors['prefix']='Bad prefix. Must have underscore (_) at the end. e.g \'ost_\''; - - //Make sure admin username is not very predictable. XXX: feels dirty but necessary - if(!$this->errors['username'] && in_array(strtolower($vars['username']),array('admin','admins','username','osticket'))) - $this->errors['username']='Bad username'; - - //MYSQL: Connect to the DB and check the version & database (create database if it doesn't exist!) - if(!$this->errors) { - if(!db_connect($vars['dbhost'],$vars['dbuser'],$vars['dbpass'])) - $this->errors['db']='Unable to connect to MySQL server. Possibly invalid login info.'; - elseif(db_version()< $this->getMySQLVersion()) - $this->errors['db']=sprintf('osTicket requires MySQL %s or better!',$this->getMySQLVersion()); - elseif(!db_select_database($vars['dbname']) && !db_create_database($vars['dbname'])) { - $this->errors['dbname']='Database doesn\'t exist'; - $this->errors['db']='Unable to create the database.'; - } elseif(!db_select_database($vars['dbname'])) { - $this->errors['dbname']='Unable to select the database'; - } - } - - //bailout on errors. - if($this->errors) return false; - - /*************** We're ready to install ************************/ - define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install. - define('PREFIX',$vars['prefix']); //Table prefix - - $schemaFile =INC_DIR.'sql/osticket-v1.7-mysql.sql'; //DB dump. - $debug = true; //XXX:Change it to true to show SQL errors. - - //Last minute checks. - if(!file_exists($schemaFile)) - $this->errors['err']='Internal Error - please make sure your download is the latest (#1)'; - elseif(!file_exists($this->getConfigFile()) || !($configFile=file_get_contents($this->getConfigFile()))) - $this->errors['err']='Unable to read config file. Permission denied! (#2)'; - elseif(!($fp = @fopen($this->getConfigFile(),'r+'))) - $this->errors['err']='Unable to open config file for writing. Permission denied! (#3)'; - elseif(!$this->load_sql_file($schemaFile,$vars['prefix'],$debug)) - $this->errors['err']='Error parsing SQL schema! Get help from developers (#4)'; - - if(!$this->errors) { - //Create admin user. - $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() ' - .', isactive=1, isadmin=1, group_id=1, dept_id=1, timezone_id=8, max_page_size=25 ' - .', email='.db_input($_POST['admin_email']) - .', firstname='.db_input($vars['fname']) - .', lastname='.db_input($vars['lname']) - .', username='.db_input($vars['username']) - .', passwd='.db_input(Passwd::hash($vars['passwd'])); - if(!mysql_query($sql) || !($uid=mysql_insert_id())) - $this->errors['err']='Unable to create admin user (#6)'; - } - - if(!$this->errors) { - //Create config settings---default settings! - //XXX: rename ostversion helpdesk_* ?? - $sql='INSERT INTO '.PREFIX.'config SET updated=NOW(), isonline=0 ' - .', default_email_id=1, alert_email_id=2, default_dept_id=1 ' - .', default_sla_id=1, default_timezone_id=8, default_template_id=1 ' - .', admin_email='.db_input($vars['admin_email']) - .', schema_signature='.db_input(md5_file($schemaFile)) - .', helpdesk_url='.db_input(URL) - .', helpdesk_title='.db_input($vars['name']); - if(!mysql_query($sql) || !($cid=mysql_insert_id())) - $this->errors['err']='Unable to create config settings (#7)'; - } - - if($this->errors) return false; //Abort on internal errors. - - - //Rewrite the config file - MUST be done last to allow for installer recovery. - $configFile= str_replace("define('OSTINSTALLED',FALSE);","define('OSTINSTALLED',TRUE);",$configFile); - $configFile= str_replace('%ADMIN-EMAIL',$vars['admin_email'],$configFile); - $configFile= str_replace('%CONFIG-DBHOST',$vars['dbhost'],$configFile); - $configFile= str_replace('%CONFIG-DBNAME',$vars['dbname'],$configFile); - $configFile= str_replace('%CONFIG-DBUSER',$vars['dbuser'],$configFile); - $configFile= str_replace('%CONFIG-DBPASS',$vars['dbpass'],$configFile); - $configFile= str_replace('%CONFIG-PREFIX',$vars['prefix'],$configFile); - $configFile= str_replace('%CONFIG-SIRI',Misc::randcode(32),$configFile); - if(!$fp || !ftruncate($fp,0) || !fwrite($fp,$configFile)) { - $this->errors['err']='Unable to write to config file. Permission denied! (#5)'; - return false; - } - @fclose($fp); - - /************* Make the system happy ***********************/ - //Create default emails! - $email = $vars['email']; - list(,$domain)=explode('@',$vars['email']); - $sql='INSERT INTO '.PREFIX.'email (`email_id`, `dept_id`, `name`,`email`,`created`,`updated`) VALUES ' - ." (1,1,'Support','$email',NOW(),NOW())" - .",(2,1,'osTicket Alerts','alerts@$domain',NOW(),NOW())" - .",(3,1,'','noreply@$domain',NOW(),NOW())"; - @mysql_query($sql); - - //Create a ticket to make the system warm and happy. - $sql='INSERT INTO '.PREFIX.'ticket SET created=NOW(), status="open", source="Web" ' - .' ,priority_id=2, dept_id=1, topic_id=1 ' - .' ,ticketID='.db_input(Misc::randNumber(6)) - .' ,email="support@osticket.com" ' - .' ,name="osTicket Support" ' - .' ,subject="osTicket Installed!"'; - if(mysql_query($sql) && ($tid=mysql_insert_id())) { - if(!($msg=file_get_contents(INC_DIR.'msg/installed.txt'))) - $msg='Congratulations and Thank you for choosing osTicket!'; - - $sql='INSERT INTO '.PREFIX.'ticket_message SET created=NOW(),source="Web" ' - .', ticket_id='.db_input($tid) - .', message='.db_input($msg); - @mysql_query($sql); - } - //TODO: create another personalized ticket and assign to admin?? - - //Log a message. - $msg="Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!"; - $sql='INSERT INTO '.PREFIX.'syslog SET created=NOW(),updated=NOW(),log_type="Debug" ' - .', title="osTicket installed!"' - .', log='.db_input($msg) - .', ip_address='.db_input($_SERVER['REMOTE_ADDR']); - @mysql_query($sql); - - return true; + function onError($error) { + return $this->setError($error); } } ?> diff --git a/setup/inc/class.upgrader.php b/setup/inc/class.upgrader.php new file mode 100644 index 0000000000000000000000000000000000000000..73af7e6066cddf41140d7bd6f2c2618431dfe83f --- /dev/null +++ b/setup/inc/class.upgrader.php @@ -0,0 +1,273 @@ +<?php +/********************************************************************* + class.upgrader.php + + osTicket Upgrader + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2012 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +require_once INC_DIR.'class.setup.php'; +require_once INC_DIR.'class.migrater.php'; + +class Upgrader extends SetupWizard { + + var $prefix; + var $sqldir; + var $signature; + + function Upgrader($signature, $prefix, $sqldir) { + + $this->signature = $signature; + $this->shash = substr($signature, 0, 8); + $this->prefix = $prefix; + $this->sqldir = $sqldir; + $this->errors = array(); + + //Init persistent state of upgrade. + $this->state = &$_SESSION['ost_upgrader'][$this->getShash()]['state']; + + //Init the task Manager. + if(!isset($_SESSION['ost_upgrader'][$this->getShash()])) + $_SESSION['ost_upgrader'][$this->getShash()]['tasks']=array(); + + //Tasks to perform - saved on the session. + $this->tasks = &$_SESSION['ost_upgrader'][$this->getShash()]['tasks']; + + //Database migrater + $this->migrater = new DatabaseMigrater($this->signature, SCHEMA_SIGNATURE, $this->sqldir); + } + + function getStops() { + return array('7be60a84' => 'migrateAttachments2DB'); + } + + function onError($error) { + + Sys::log(LOG_ERR, 'Upgrader Error', $error); + $this->setError($error); + $this->setState('aborted'); + } + + function isUpgradable() { + return (!$this->isAborted() && $this->getNextPatch()); + } + + function isAborted() { + return !strcasecmp($this->getState(), 'aborted'); + } + + function getSchemaSignature() { + return $this->signature; + } + + function getShash() { + return $this->shash; + } + + function getTablePrefix() { + return $this->prefix; + } + + function getSQLDir() { + return $this->sqldir; + } + + function getState() { + return $this->state; + } + + function setState($state) { + $this->state = $state; + } + + function getPatches() { + return $this->migrater->getPatches(); + } + + function getNextPatch() { + $p = $this->getPatches(); + return (count($p)) ? $p[0] : false; + } + + function getNextVersion() { + if(!$patch=$this->getNextPatch()) + return '(Latest)'; + + $info = $this->readPatchInfo($patch); + return $info['version']; + } + + function readPatchInfo($patch) { + $info = array(); + if (preg_match('/\*(.*)\*/', file_get_contents($patch), $matches)) { + if (preg_match('/@([\w\d_-]+)\s+(.*)$/', $matches[0], $matches2)) + foreach ($matches2 as $match) + $info[$match[0]] = $match[1]; + } + if (!isset($info['version'])) + $info['version'] = substr(basename($patch), 9, 8); + return $info; + } + + function getNextAction() { + + $action='Upgrade osTicket to '.$this->getVersion(); + if($this->getNumPendingTasks() && ($task=$this->getNextTask())) { + $action = $task['desc']; + if($task['status']) //Progress report... + $action.=' ('.$task['status'].')'; + } elseif($this->isUpgradable() && ($nextversion = $this->getNextVersion())) { + $action = "Upgrade to $nextversion"; + } + + return $action; + } + + function getNumPendingTasks() { + + return count($this->getPendingTasks()); + } + + function getPendingTasks() { + + $pending=array(); + if(($tasks=$this->getTasks())) { + foreach($tasks as $k => $task) { + if(!$task['done']) + $pending[$k] = $task; + } + } + + return $pending; + } + + function getTasks() { + return $this->tasks; + } + + function getNextTask() { + + if(!($tasks=$this->getPendingTasks())) + return null; + + return current($tasks); + } + + function removeTask($tId) { + + if(isset($this->tasks[$tId])) + unset($this->tasks[$tId]); + + return (!$this->tasks[$tId]); + } + + function setTaskStatus($tId, $status) { + if(isset($this->tasks[$tId])) + $this->tasks[$tId]['status'] = $status; + } + + function doTasks() { + + if(!($tasks=$this->getPendingTasks())) + return true; //Nothing to do. + + foreach($tasks as $k => $task) { + if(call_user_func(array($this, $task['func']), $k)===0) { + $this->tasks[$k]['done'] = true; + } else { //Task has pending items to process. + break; + } + } + + return (!$this->getPendingTasks()); + } + + function upgrade() { + + if($this->getPendingTasks() || !($patches=$this->getPatches())) + return false; + + foreach ($patches as $patch) { + if (!$this->load_sql_file($patch, $this->getTablePrefix())) + return false; + + //clear previous patch info - + unset($_SESSION['ost_upgrader'][$this->getShash()]); + + $phash = substr(basename($patch), 0, 17); + + //Log the patch info + $logMsg = "Patch $phash applied "; + if(($info = $this->readPatchInfo($patch)) && $info['version']) + $logMsg.= ' ('.$info['version'].') '; + + Sys::log(LOG_DEBUG, 'Upgrader - Patch applied', $logMsg); + + //Check if the said patch has scripted tasks + if(!($tasks=$this->getTasksForPatch($phash))) + continue; + + //We have work to do... set the tasks and break. + $shash = substr($phash, 9, 8); + $_SESSION['ost_upgrader'][$shash]['tasks'] = $tasks; + $_SESSION['ost_upgrader'][$shash]['state'] = 'upgrade'; + break; + } + + return true; + + } + + function getTasksForPatch($phash) { + + $tasks=array(); + switch($phash) { //Add patch specific scripted tasks. + case 'd4fe13b1-7be60a84': //V1.6 ST- 1.7 * + $tasks[] = array('func' => 'migrateAttachments2DB', + 'desc' => 'Migrating attachments to database, it might take a while depending on the number of files.'); + break; + } + + //Check if cleanup p + $file=$this->getSQLDir().$phash.'.cleanup.sql'; + if(file_exists($file)) + $tasks[] = array('func' => 'cleanup', 'desc' => 'Post-upgrade cleanup!'); + + + return $tasks; + } + + /************* TASKS **********************/ + function cleanup($tId=0) { + + $file=$this->getSQLDir().$this->getShash().'-cleanup.sql'; + if(!file_exists($file)) //No cleanup script. + return 0; + + //We have a cleanup script ::XXX: Don't abort on error? + if($this->load_sql_file($file, $this->getTablePrefix(), false, true)) + return 0; + + //XXX: ??? + return false; + } + + + function migrateAttachments2DB($tId=0) { + echo "Process attachments here - $tId"; + $att_migrater = new AttachmentMigrater(); + $att_migrater->start_migration(); + # XXX: Loop here (with help of task manager) + $att_migrater->do_batch(); + return 0; + } +} +?> diff --git a/setup/inc/sql/02decaa2-60fcbee1.patch.sql b/setup/inc/sql/02decaa2-60fcbee1.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..40dc644b3df64b0e103a3a7c16ed3c835a4bfae3 --- /dev/null +++ b/setup/inc/sql/02decaa2-60fcbee1.patch.sql @@ -0,0 +1,6 @@ +-- Update all temlates with the new wording. +UPDATE `%TABLE_PREFIX%email_template` + SET `ticket_overlimit_body` = '%name\r\n\r\nYou have reached the maximum number of open tickets allowed.\r\n\r\nTo be able to open another ticket, one of your pending tickets must be closed. To update or add comments to an open ticket simply login using the link below.\r\n\r\n%url/view.php?e=%email\r\n\r\nThank you.\r\n\r\nSupport Ticket System'; + +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='60fcbee1da3180d1b690187aa5006c88'; diff --git a/setup/inc/sql/522e5b78-02decaa2.patch.sql b/setup/inc/sql/522e5b78-02decaa2.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..78c951d69cc10f255ffb3e4ba85fcf93dae6db58 --- /dev/null +++ b/setup/inc/sql/522e5b78-02decaa2.patch.sql @@ -0,0 +1,10 @@ +/** + * @version v1.7-DPR2-P2 + */ +UPDATE `%TABLE_PREFIX%sla` + SET `created` = NOW(), + `updated` = NOW() + WHERE `created` IS NULL; + +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='02decaa20c10c9615558762018e25507'; diff --git a/setup/inc/sql/60fcbee1-f8856d56.patch.sql b/setup/inc/sql/60fcbee1-f8856d56.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..2b7e48599816e88702f709f50557189b160a727e --- /dev/null +++ b/setup/inc/sql/60fcbee1-f8856d56.patch.sql @@ -0,0 +1,19 @@ +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_event`; +CREATE TABLE `%TABLE_PREFIX%ticket_event` ( + `ticket_id` int(11) unsigned NOT NULL default '0', + `staff_id` int(11) unsigned NOT NULL, + `team_id` int(11) unsigned NOT NULL, + `dept_id` int(11) unsigned NOT NULL, + `topic_id` int(11) unsigned NOT NULL, + `state` enum('created','closed','reopened','assigned','transferred','overdue') NOT NULL, + `staff` varchar(255) NOT NULL default 'SYSTEM', + `timestamp` datetime NOT NULL, + KEY `ticket_state` (`ticket_id`, `state`, `timestamp`), + KEY `ticket_stats` (`timestamp`, `state`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_history`; +DROP TABLE IF EXISTS `%TABLE_PREFIX%history`; + +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='f8856d56e51c5cc3416389de78b54515'; diff --git a/setup/inc/sql/7be60a84-522e5b78.patch.sql b/setup/inc/sql/7be60a84-522e5b78.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..acdaf7d0d3c1f8baaedd40bd68e33b01b97e72c9 --- /dev/null +++ b/setup/inc/sql/7be60a84-522e5b78.patch.sql @@ -0,0 +1,9 @@ +/** + * @version v1.7-DPR1 (P1) + */ +UPDATE `%TABLE_PREFIX%email_template` + SET `ticket_overlimit_subj` = 'Open Tickets Limit Reached' + WHERE `tpl_id` = 1 AND `cfg_id` = 1; + +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='522e5b783c2824c67222260ee22baa93'; diff --git a/setup/inc/sql/abe9c0cb-bbb021fb.patch.sql b/setup/inc/sql/abe9c0cb-bbb021fb.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..0a3248db4569aebd2ce75eed39cfff3da68d559c --- /dev/null +++ b/setup/inc/sql/abe9c0cb-bbb021fb.patch.sql @@ -0,0 +1,14 @@ +/** + * Add an 'annulled' column to the %ticket_event table to assist in tracking + * real statistics for reopened and closed tickets -- the events should not + * count more than one time. + * + * @version 1.7-dpr3 ticket-event-annul + */ + +ALTER TABLE `%TABLE_PREFIX%ticket_event` + ADD `annulled` tinyint(1) NOT NULL DEFAULT '0' AFTER `staff`; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='bbb021fbeb377ca66b6997b77e0167cc'; diff --git a/setup/inc/sql/bbb021fb-49478749.patch.sql b/setup/inc/sql/bbb021fb-49478749.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..8bd81f32016f936848f790ef325e1613317cc3fc --- /dev/null +++ b/setup/inc/sql/bbb021fb-49478749.patch.sql @@ -0,0 +1,9 @@ +/** + * Transitional patch - FIX on the INSTALLER schema + * + * @version 1.7-dpr3 installerfix + */ + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='49478749dc680eef08b7954bd568cfd1'; diff --git a/setup/inc/sql/f8856d56-abe9c0cb.patch.sql b/setup/inc/sql/f8856d56-abe9c0cb.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..a5f54aa8516aaaaf268e363b8ae58674725b61af --- /dev/null +++ b/setup/inc/sql/f8856d56-abe9c0cb.patch.sql @@ -0,0 +1,109 @@ +/** + * Merge ticket thread tables into one + * + * Replace the ticket_{message,response,note} tables with a single + * ticket_thread table that will contain data for all three current message + * types. This simplifies much of the ticket thread code and paves the way + * for other types of messages in the future. + * + * This patch automagically moves the data from the three federated tables + * into the one combined table. + */ +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_thread`; +CREATE TABLE `%TABLE_PREFIX%ticket_thread` ( + `id` int(11) unsigned NOT NULL auto_increment, + `pid` int(11) unsigned NOT NULL default '0', + `ticket_id` int(11) unsigned NOT NULL default '0', + `staff_id` int(11) unsigned NOT NULL default '0', + `thread_type` enum('M','R','N') NOT NULL, + `poster` varchar(128) NOT NULL default '', + `source` varchar(32) NOT NULL default '', + `title` varchar(255), + `body` text NOT NULL, + `ip_address` varchar(64) NOT NULL default '', + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + -- Temporary columns for conversion + `old_pk` int(11) unsigned NOT NULL, + `old_pid` int(11) unsigned, + PRIMARY KEY (`id`), + KEY `ticket_id` (`ticket_id`), + KEY `staff_id` (`staff_id`), + FULLTEXT KEY `body` (`body`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_email_info`; +CREATE TABLE `%TABLE_PREFIX%ticket_email_info` ( + `message_id` int(11) unsigned NOT NULL, + `email_mid` varchar(255) NOT NULL, + `headers` text, + KEY `message_id` (`email_mid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- Transfer messages +INSERT INTO `%TABLE_PREFIX%ticket_thread` + (`ticket_id`, `thread_type`, `body`, `ip_address`, + `created`, `updated`, `old_pk`) + SELECT `ticket_id`, 'M', `message`, `ip_address`, + `created`, COALESCE(`updated`, NOW()), `msg_id` + FROM `%TABLE_PREFIX%ticket_message`; + +-- Transfer responses +INSERT INTO `%TABLE_PREFIX%ticket_thread` + (`ticket_id`, `staff_id`, `thread_type`, `poster`, `body`, `ip_address`, + `created`, `updated`, `old_pk`, `old_pid`) + SELECT `ticket_id`, `staff_id`, 'R', `staff_name`, `response`, `ip_address`, + `created`, COALESCE(`updated`, NOW()), `response_id`, `msg_id` + FROM `%TABLE_PREFIX%ticket_response`; + +-- Connect responses to (new) messages +CREATE TABLE `%TABLE_PREFIX%T_resp_links` + SELECT `id`, `old_pk`, `old_pid` FROM `%TABLE_PREFIX%ticket_thread`; + +UPDATE `%TABLE_PREFIX%ticket_thread` + SET `pid` = ( SELECT T2.`id` FROM `%TABLE_PREFIX%T_resp_links` T2 + WHERE `old_pid` = T2.`old_pk` ) + WHERE `thread_type` = 'R' + AND `old_pid` IS NOT NULL; + +DROP TABLE `%TABLE_PREFIX%T_resp_links`; + +-- Transfer notes +INSERT INTO `%TABLE_PREFIX%ticket_thread` + (`ticket_id`, `staff_id`, `thread_type`, `body`, `title`, + `source`, `poster`, `created`, `updated`, `old_pk`) + SELECT `ticket_id`, `staff_id`, 'N', `note`, `title`, + `source`, ( SELECT CONCAT_WS(' ', T2.`firstname`, T2.`lastname`) + FROM `%TABLE_PREFIX%staff` T2 + WHERE T2.`staff_id` = `staff_id` ), + `created`, NOW(), `note_id` + FROM `%TABLE_PREFIX%ticket_note`; + +-- Transfer email information from messages +INSERT INTO `%TABLE_PREFIX%ticket_email_info` + (`message_id`, `email_mid`, `headers`) + SELECT ( SELECT T2.`id` FROM `%TABLE_PREFIX%ticket_thread` T2 + WHERE `msg_id` = T2.`old_pk` + AND `thread_type` = 'M' ), + `messageId`, `headers` + FROM `%TABLE_PREFIX%ticket_message` + WHERE `messageId` IS NOT NULL; + +-- Update attachment table +UPDATE `%TABLE_PREFIX%ticket_attachment` + SET `ref_id` = ( SELECT T2.`id` FROM `%TABLE_PREFIX%ticket_thread` T2 + WHERE `ref_id` = T2.`old_pk` + AND `ref_type` = T2.`thread_type` ); + +-- Drop temporary columns +ALTER TABLE `%TABLE_PREFIX%ticket_thread` DROP COLUMN `old_pk`; +ALTER TABLE `%TABLE_PREFIX%ticket_thread` DROP COLUMN `old_pid`; + +-- Drop old tables +DROP TABLE `%TABLE_PREFIX%ticket_message`; +DROP TABLE `%TABLE_PREFIX%ticket_response`; +DROP TABLE `%TABLE_PREFIX%ticket_note`; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='abe9c0cb845be52c10fcd7b3e626a589'; diff --git a/setup/inc/sql/osticket-v1.7-mysql.sql b/setup/inc/sql/osticket-v1.7-mysql.sql index df11daf8612702b46a2c9fda0be56a2fe16f2445..33b034c763f8197e6d21d267abe50c58d2a37738 100644 --- a/setup/inc/sql/osticket-v1.7-mysql.sql +++ b/setup/inc/sql/osticket-v1.7-mysql.sql @@ -598,48 +598,29 @@ CREATE TABLE `%TABLE_PREFIX%ticket_lock` ( KEY `staff_id` (`staff_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_history`; -CREATE TABLE `%TABLE_PREFIX%ticket_history` ( +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_email_info`; +CREATE TABLE `%TABLE_PREFIX%ticket_email_info` ( + `message_id` int(11) unsigned NOT NULL, + `email_mid` varchar(255) NOT NULL, + `headers` text, + KEY `message_id` (`email_mid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_event`; +CREATE TABLE `%TABLE_PREFIX%ticket_event` ( `ticket_id` int(11) unsigned NOT NULL default '0', - `state` enum('opened','closed','assigned','transferred','overdue') NOT NULL, + `staff_id` int(11) unsigned NOT NULL, + `team_id` int(11) unsigned NOT NULL, + `dept_id` int(11) unsigned NOT NULL, + `topic_id` int(11) unsigned NOT NULL, + `state` enum('created','closed','reopened','assigned','transferred','overdue') NOT NULL, `staff` varchar(255) NOT NULL default 'SYSTEM', + `annulled` tinyint(1) unsigned NOT NULL default '0', `timestamp` datetime NOT NULL, KEY `ticket_state` (`ticket_id`, `state`, `timestamp`), KEY `ticket_stats` (`timestamp`, `state`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_message`; -CREATE TABLE `%TABLE_PREFIX%ticket_message` ( - `msg_id` int(11) unsigned NOT NULL auto_increment, - `ticket_id` int(11) unsigned NOT NULL default '0', - `messageId` varchar(255) default NULL, - `message` text NOT NULL, - `headers` text, - `source` varchar(16) default NULL, - `ip_address` varchar(16) default NULL, - `created` datetime NOT NULL, - `updated` datetime default NULL, - PRIMARY KEY (`msg_id`), - KEY `ticket_id` (`ticket_id`), - KEY `msgId` (`messageId`), - FULLTEXT KEY `message` (`message`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; - -DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_note`; -CREATE TABLE `%TABLE_PREFIX%ticket_note` ( - `note_id` int(11) unsigned NOT NULL auto_increment, - `ticket_id` int(11) unsigned NOT NULL default '0', - `staff_id` int(10) unsigned NOT NULL default '0', - `source` varchar(32) NOT NULL default '', - `title` varchar(255) NOT NULL default 'Generic Intermal Notes', - `note` text NOT NULL, - `created` datetime NOT NULL, - PRIMARY KEY (`note_id`), - KEY `ticket_id` (`ticket_id`), - KEY `staff_id` (`staff_id`), - FULLTEXT KEY `note` (`note`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; - DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_priority`; CREATE TABLE `%TABLE_PREFIX%ticket_priority` ( `priority_id` tinyint(4) NOT NULL auto_increment, @@ -660,22 +641,24 @@ INSERT INTO `%TABLE_PREFIX%ticket_priority` (`priority_id`, `priority`, `priorit (3, 'high', 'High', '#FEE7E7', 2, 1), (4, 'emergency', 'Emergency', '#FEE7E7', 1, 0); -DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_response`; -CREATE TABLE `%TABLE_PREFIX%ticket_response` ( - `response_id` int(11) unsigned NOT NULL auto_increment, - `msg_id` int(11) unsigned NOT NULL default '0', +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_thread`; +CREATE TABLE `%TABLE_PREFIX%ticket_thread` ( + `id` int(11) unsigned NOT NULL auto_increment, + `pid` int(11) unsigned NOT NULL default '0', `ticket_id` int(11) unsigned NOT NULL default '0', `staff_id` int(11) unsigned NOT NULL default '0', - `staff_name` varchar(32) NOT NULL default '', - `response` text NOT NULL, - `ip_address` varchar(16) NOT NULL default '', + `thread_type` enum('M','R','N') NOT NULL, + `poster` varchar(128) NOT NULL default '', + `source` varchar(32) NOT NULL default '', + `title` varchar(255), + `body` text NOT NULL, + `ip_address` varchar(64) NOT NULL default '', `created` datetime NOT NULL, `updated` datetime NOT NULL, - PRIMARY KEY (`response_id`), + PRIMARY KEY (`id`), KEY `ticket_id` (`ticket_id`), - KEY `msg_id` (`msg_id`), KEY `staff_id` (`staff_id`), - FULLTEXT KEY `response` (`response`) + FULLTEXT KEY `body` (`body`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%timezone`; diff --git a/setup/inc/sql/osticket-v1.7-mysql.sql.md5 b/setup/inc/sql/osticket-v1.7-mysql.sql.md5 new file mode 100644 index 0000000000000000000000000000000000000000..f27a74d5b65a34a55b6b03a5a5624c2adcf7fccf --- /dev/null +++ b/setup/inc/sql/osticket-v1.7-mysql.sql.md5 @@ -0,0 +1 @@ +49478749dc680eef08b7954bd568cfd1 diff --git a/setup/inc/sql/v1.6st-1.7-upgrade-mysql.sql b/setup/inc/sql/v1.6st-1.7-upgrade-mysql.sql index efeb8de6f26eb4e4c4a5e10140147c5b1ebdd3c6..3a2acf2295170c9b6263b33f05d649c378ed5dbb 100644 --- a/setup/inc/sql/v1.6st-1.7-upgrade-mysql.sql +++ b/setup/inc/sql/v1.6st-1.7-upgrade-mysql.sql @@ -26,10 +26,14 @@ ALTER TABLE `%TABLE_PREFIX%ticket` 'Web', 'Email', 'Phone', 'API', 'Other') NOT NULL DEFAULT 'Other'; -- Add table for ticket history (statistics) tracking -DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_history`; -CREATE TABLE `%TABLE_PREFIX%ticket_history` ( +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_event`; +CREATE TABLE `%TABLE_PREFIX%ticket_event` ( `ticket_id` int(11) unsigned NOT NULL default '0', - `state` enum('opened','closed','assigned','transferred','overdue') NOT NULL, + `staff_id` int(11) unsigned NOT NULL, + `team_id` int(11) unsigned NOT NULL, + `dept_id` int(11) unsigned NOT NULL, + `topic_id` int(11) unsigned NOT NULL, + `state` enum('created','closed','reopened','assigned','transferred','overdue') NOT NULL, `staff` varchar(255) NOT NULL default 'SYSTEM', `timestamp` datetime NOT NULL, KEY `ticket_state` (`ticket_id`, `state`, `timestamp`), diff --git a/setup/inc/upgrade-aborted.inc.php b/setup/inc/upgrade-aborted.inc.php index 8d8f64aa831841dd9431f017c7289760ba18448e..8624590d35710a42906952afab82b2688f028315 100644 --- a/setup/inc/upgrade-aborted.inc.php +++ b/setup/inc/upgrade-aborted.inc.php @@ -6,17 +6,18 @@ if(!defined('SETUPINC')) die('Kwaheri!'); <div id="intro"> <p>Upgrade aborted due to errors. Any errors at this stage are fatal. Please note the error(s), if any, when contacting us.<p> <?php - if($_SESSION['upgrader']['errors']) { - $errors=$_SESSION['upgrader']['errors']; + if($upgrader && ($errors=$upgrader->getErrors())) { if($errors['err']) echo sprintf('<b><font color="red">%s</font></b>',$errors['err']); - echo '<ul class="error">'; unset($errors['err']); - foreach($errors as $k=>$error) + foreach($errors as $k => $error) echo sprintf('<li>%s</li>',$error); echo '</ul>'; - } ?> + } else { + echo '<b><font color="red">Internal error occurred - get technical help.</font></b>'; + } + ?> <p>Please, refer to the <a target="_blank" href="http://osticket.com/wiki/Upgrade_and_Migration">Upgrade Guide</a> on the wiki for more information.</p> </div> <p><strong>Need Help?</strong> We provide <a target="_blank" href="http://osticket.com/support/professional_services.php"><u>professional upgrade services</u></a> and commercial support. <a target="_blank" href="http://osticket.com/support/">Contact us</a> today for expedited help.</p> diff --git a/setup/inc/upgrade-attachments.inc.php b/setup/inc/upgrade-attachments.inc.php deleted file mode 100644 index c8155d32c672e358cb068d79720a076327a88706..0000000000000000000000000000000000000000 --- a/setup/inc/upgrade-attachments.inc.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -if(!defined('SETUPINC')) die('Kwaheri!'); -$msg = $_SESSION['ost_upgrader']['msg']; -?> -<div id="main"> - <h1>Attachments Migration</h1> - <p>We're almost done! We're now migrating attachments to the database, it might take a while dending on the number of files in your database.<p> - <p style="color:#FF7700;font-weight:bold;">We have to migrate files in batches for technical reasons.</p> - <p>Please don't cancel or close the browser.</p> - - <div id="bar"> - <form method="post" action="upgrade.php" id="attachments"> - <input type="hidden" name="s" value="cleanup"> - <input class="btn" type="submit" name="submit" value="Next Batch"> - </form> - - </div> -</div> -<div id="sidebar"> - <h3>Upgrade Tips</h3> - <p>1. Be patient the process will take a few minutes.</p> - <p>2. If you experience any problems, you can always restore your files/dabase backup.</p> - <p>3. We can help, feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p> -</div> -<div id="overlay"></div> - <div id="loading"> - <h4>Moving attachments</h4> - <br> - Please wait... while we migrate attachments! - <br><br> - <div id="msg" style="font-weight: bold;"><?php echo Format::htmlchars($msg); ?></div> - </div> diff --git a/setup/inc/upgrade-cleanup.inc.php b/setup/inc/upgrade-cleanup.inc.php deleted file mode 100644 index f8406ae533a9efbce73e864239d749b586e8f66e..0000000000000000000000000000000000000000 --- a/setup/inc/upgrade-cleanup.inc.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -if(!defined('SETUPINC')) die('Kwaheri!'); - -?> - <div id="main"> - <h1>osTicket Upgrade</h1> - <div id="intro"> - <p>We're almost done! Please don't cancel or close the browser, any errors at this stage will be fatal.</p> - </div> - <h2>Cleanup: Step 2 of 2</h2> - <p>The upgrade wizard will now attempt to do post upgrade cleanup. It might take a while dending on the size of your database. </p> - <ul> - <li>Setting Changes</li> - <li>Attachment Migration</li> - <li>Database Optimization</li> - </ul> - <div id="bar"> - <form method="post" action="upgrade.php" id="cleanup"> - <input type="hidden" name="s" value="cleanup"> - <input class="btn" type="submit" name="submit" value="Do It Now!"> - </form> - </div> - </div> - <div id="sidebar"> - <h3>Upgrade Tips</h3> - <p>1. Be patient the process will take a couple of seconds.</p> - <p>2. If you experience any problems, you can always restore your files/dabase backup.</p> - <p>3. We can help, feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p> - </div> - - <div id="overlay"></div> - <div id="loading"> - <h4>Doing serious stuff!</h4> - <br> - Please wait... while we do post-upgrade cleanup! - <br><br> - <div id="msg" style="font-weight: bold;"></div> - </div> diff --git a/setup/inc/upgrade-core.inc.php b/setup/inc/upgrade-core.inc.php deleted file mode 100644 index ec42e50437537ea01931484a18e86c902ba909b1..0000000000000000000000000000000000000000 --- a/setup/inc/upgrade-core.inc.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -if(!defined('SETUPINC')) die('Kwaheri!'); - -?> - <div id="main"> - <h1>osTicket Upgrade</h1> - <div id="intro"> - <p>Thank you for taking the time to upgrade your osTicket intallation!</p> - <p>Please don't cancel or close the browser, any errors at this - stage will be fatal.</p> - </div> - <h2>Base upgrade: Step 1 of 2</h2> - <p>The upgrade wizard will now attempt to upgrade your database and core settings!</p> - <ul> - <li>Database enhancements</li> - <li>New and updated features</li> - <li>Enhance settings and security</li> - </ul> - <div id="bar"> - <form method="post" action="upgrade.php" id="upgrade"> - <input type="hidden" name="s" value="upgrade"> - <input class="btn" type="submit" name="submit" value="Do It Now!"> - </form> - </div> - </div> - <div id="sidebar"> - <h3>Upgrade Tips</h3> - <p>1. Be patient the process will take a couple of seconds.</p> - <p>2. If you experience any problems, you can always restore your files/dabase backup.</p> - <p>3. We can help, feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p> - </div> - - <div id="overlay"></div> - <div id="loading"> - <h4>Doing serious stuff!</h4> - Please wait... while we upgrade your osTicket installation! - <div id="udb"><br><b>Smile!</b></div> - </div> diff --git a/setup/inc/upgrade-prereq.inc.php b/setup/inc/upgrade-prereq.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..5f60cece25500679de83b01981ea05ac634949e9 --- /dev/null +++ b/setup/inc/upgrade-prereq.inc.php @@ -0,0 +1,50 @@ +<?php +if(!defined('SETUPINC')) die('Kwaheri!'); + +?> + <div id="main"> + <h1>osTicket Upgrade!</h1> + <font color="red"><b><?php echo $errors['err']; ?></b></font> + <div id="intro"> + <p>Thank you for being a loyal osTicket user!</p> + <p>The upgrade wizard will guide you every step of the way in the upgrade process. While we try to ensure that the upgrade process is straightforward and painless, we can't guarantee it will be the case for every user.</p> + </div> + <h2>Getting ready!</h2> + <p>Before we begin, we'll check your server configuration to make sure you meet the minimum requirements to run the latest version of osTicket.</p> + <h3>Prerequisites: <font color="red"><?php echo $errors['prereq']; ?></font></h3> + These items are necessary in order to use osTicket the latest version of osTicket. + <ul class="progress"> + <li class="<? echo $upgrader->check_php()?'yes':'no'; ?>"> + PHP v4.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li> + <li class="<? echo $upgrader->check_mysql()?'yes':'no'; ?>"> + MySQL v4.4 or greater - (<small><b><?php echo extension_loaded('mysql')?'module loaded':'missing!'; ?></b></small>)</li> + </ul> + <h3>Higly Recommended:</h3> + We hightly recommend that you follow the steps below. + <ul> + <li>Take osTicket offline momentarily during upgrade.</li> + <li>Backup the current database, if you haven't done so already.</li> + <li>Be patient the upgrade process will take a couple of seconds.</li> + </ul> + <div id="bar"> + <form method="post" action="upgrade.php" id="prereq"> + <input type="hidden" name="s" value="prereq"> + <input class="btn" type="submit" name="submit" value="Start Upgrade Now »"> + </form> + </div> + </div> + <div id="sidebar"> + <h3>Upgrade Tips</h3> + <p>1. Remember to backup your osTicket database</p> + <p>2. Take osTicket offline momentarily</p> + <p>3. If you experience any problems, you can always restore your files/dabase backup.</p> + <p>4. We can help, feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p> + + </div> + + <div id="overlay"></div> + <div id="loading"> + <h4>Doing stuff!</h4> + Please wait... while we upgrade your osTicket installation! + <div id="udb"></div> + </div> diff --git a/setup/inc/upgrade.inc.php b/setup/inc/upgrade.inc.php index e5b8ba77bef7da18c547820031d1ca294b2b4dea..6f05cd169c66d49163908d8a6c6bef9d5b9a1a18 100644 --- a/setup/inc/upgrade.inc.php +++ b/setup/inc/upgrade.inc.php @@ -1,50 +1,40 @@ <?php -if(!defined('SETUPINC')) die('Kwaheri!'); +if(!defined('SETUPINC') || !$upgrader) die('Kwaheri!'); +$action=$upgrader->getNextAction(); ?> <div id="main"> - <h1>osTicket Upgrade!</h1> - <font color="red"><b><?php echo $errors['err']; ?></b></font> + <h1>osTicket Upgrade</h1> <div id="intro"> - <p>Thank you for being a loyal osTicket user!</p> - <p>The upgrade wizard will guide you every step of the way in the upgrade process. While we try to ensure that the upgrade process is straightforward and painless, we can't guarantee it will be the case for every user.</p> + <p>Thank you for taking the time to upgrade your osTicket intallation!</p> + <p>Please don't cancel or close the browser, any errors at this + stage will be fatal.</p> </div> - <h2>Getting ready!</h2> - <p>Before we begin, we'll check your server configuration to make sure you meet the minimum requirements to run the latest version of osTicket.</p> - <h3>Prerequisites: <font color="red"><?php echo $errors['prereq']; ?></font></h3> - These items are necessary in order to use osTicket the latest version of osTicket. - <ul class="progress"> - <li class="<? echo $upgrader->check_php()?'yes':'no'; ?>"> - PHP v4.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li> - <li class="<? echo $upgrader->check_mysql()?'yes':'no'; ?>"> - MySQL v4.4 or greater - (<small><b><?php echo extension_loaded('mysql')?'module loaded':'missing!'; ?></b></small>)</li> - </ul> - <h3>Higly Recommended:</h3> - We hightly recommend that you follow the steps below. + <h2><?php echo $action ?></h2> + <p>The upgrade wizard will now attempt to upgrade your database and core settings!</p> <ul> - <li>Take osTicket offline momentarily during upgrade.</li> - <li>Backup the current database, if you haven't done so already.</li> - <li>Be patient the upgrade process will take a couple of seconds.</li> + <li>Database enhancements</li> + <li>New and updated features</li> + <li>Enhance settings and security</li> </ul> <div id="bar"> <form method="post" action="upgrade.php" id="upgrade"> - <input type="hidden" name="s" value="prereq"> - <input class="btn" type="submit" name="submit" value="Start Upgrade Now »"> + <input type="hidden" name="s" value="upgrade"> + <input type="hidden" name="sh" value="<?php echo $upgrader->getSchemaSignature(); ?>"> + <input class="btn" type="submit" name="submit" value="Do It Now!"> </form> </div> </div> <div id="sidebar"> <h3>Upgrade Tips</h3> - <p>1. Remember to backup your osTicket database</p> - <p>2. Take osTicket offline momentarily</p> - <p>3. If you experience any problems, you can always restore your files/dabase backup.</p> - <p>4. We can help, feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p> - + <p>1. Be patient the process will take a couple of seconds.</p> + <p>2. If you experience any problems, you can always restore your files/dabase backup.</p> + <p>3. We can help, feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p> </div> <div id="overlay"></div> <div id="loading"> - <h4>Doing stuff!</h4> + <h4><?php echo $action; ?></h4> Please wait... while we upgrade your osTicket installation! - <div id="udb"></div> + <div id="msg" style="font-weight: bold;padding-top:10px;">Smile!</div> </div> diff --git a/setup/install.php b/setup/install.php index 27b1388933d03ef9c0db5690dd147213848d750f..d019598e55de7099c9dbb517b4a9aa1663886ad0 100644 --- a/setup/install.php +++ b/setup/install.php @@ -15,6 +15,9 @@ **********************************************************************/ require('setup.inc.php'); +require_once INC_DIR.'class.installer.php'; + + //define('OSTICKET_CONFIGFILE','../include/ost-config.php'); //osTicket config file full path. define('OSTICKET_CONFIGFILE','../include/ost-config.php'); //XXX: Make sure the path is corrent b4 releasing. diff --git a/setup/js/setup.js b/setup/js/setup.js index 5d274ea3198bbec310c20abef16ac68df51ee270..b1f8e08c3aa48397206c43ad854fc3bf4815473c 100644 --- a/setup/js/setup.js +++ b/setup/js/setup.js @@ -13,30 +13,30 @@ jQuery(function($) { left : ($(window).width() / 2 - 160) }); - $('form#install, form#upgrade, form#attachments').submit(function(e) { + $('form#install').submit(function(e) { $('input[type=submit]', this).attr('disabled', 'disabled'); $('#overlay, #loading').show(); return true; }); - $('form#cleanup').submit(function(e) { + $('form#upgrade').submit(function(e) { e.preventDefault(); var form = $(this); $('input[type=submit]', this).attr('disabled', 'disabled'); $('#overlay, #loading').show(); - doCleanup('upgrade',form.attr('action')); + doTasks('upgrade.php',form.serialize()); + return false; }); - - function doCleanup(type,url) { + function doTasks(url, data) { function _lp(count) { $.ajax({ type: 'GET', - url: 'cleanup.php', + url: 'p.php', async: true, cache: false, - data: {c:count,type:type}, + data: data, dataType: 'text', success: function(res) { if (res) { @@ -45,16 +45,17 @@ jQuery(function($) { }, statusCode: { 200: function() { - setTimeout(function() { _lp(count+1); },2); + setTimeout(function() { _lp(count+1); }, 2); }, 304: function() { - $('#loading #msg').html("We're done... "); - setTimeout(function() { location.href =url;},1000); + $('#loading #msg').html("We're done... cleaning up!"); + setTimeout(function() { location.href =url;}, 3000); } }, - error: function(){ - alert("Something went wrong"); + error: function() { + $('#loading #msg').html("Something went wrong"); + setTimeout(function() { location.href =url;}, 1000); } }); }; diff --git a/setup/p.php b/setup/p.php new file mode 100644 index 0000000000000000000000000000000000000000..babeebb866f198e7fc78516a34133164d72a9b47 --- /dev/null +++ b/setup/p.php @@ -0,0 +1,69 @@ +<?php +/********************************************************************* + upgrader.php + + osTicket Upgrader Helper - called via ajax. + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2012 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +function staffLoginPage($msg) { + Http::response(403, $msg?$msg:'Access Denied'); + exit; +} + +require '../scp/staff.inc.php'; +if(!$thisstaff or !$thisstaff->isAdmin()) { + staffLoginPage('Admin Access Required!'); + exit; +} + +define('SETUPINC', true); +define('INC_DIR', './inc/'); +define('SQL_DIR', INC_DIR.'sql/'); + +require_once INC_DIR.'class.upgrader.php'; + + +$upgrader = new Upgrader($cfg->getSchemaSignature(), TABLE_PREFIX, SQL_DIR); + +//Just report the next action on the first call. +if(!$_SESSION['ost_upgrader'][$upgrader->getShash()]['progress']) { + $_SESSION['ost_upgrader'][$upgrader->getShash()]['progress'] = $upgrader->getNextAction(); + Http::response(200, $upgrader->getNextAction()); + exit; +} + +if($upgrader->getNumPendingTasks()) { + if($upgrader->doTasks() && !$upgrader->getNumPendingTasks() && $cfg->isUpgradePending()) { + //Just reporting done...with tasks - break in between patches! + header("HTTP/1.1 304 Not Modified"); + exit; + } +} elseif($cfg->isUpgradePending() && $upgrader->isUpgradable()) { + $version = $upgrader->getNextVersion(); + if($upgrader->upgrade()) { + //We're simply reporting progress here - call back will report next action' + Http::response(200, "Upgraded to $version ... post-upgrade checks!"); + exit; + } +} elseif(!$cfg->isUpgradePending()) { + $upgrader->setState('done'); + session_write_close(); + header("HTTP/1.1 304 Not Modified"); + exit; +} + +if($upgrader->isAborted() || $upgrader->getErrors()) { + Http::response(416, "We have a problem ... wait a sec."); + exit; +} + +Http::response(200, $upgrader->getNextAction()); +?> diff --git a/setup/setup.inc.php b/setup/setup.inc.php index f2af40af61e1b7e94b9ae915eb1e81e103560594..44b2ad4d593f5521b66ad170c9e801a817cf74f1 100644 --- a/setup/setup.inc.php +++ b/setup/setup.inc.php @@ -52,10 +52,8 @@ ini_set('include_path', './'.PATH_SEPARATOR.INC_DIR.PATH_SEPARATOR.INCLUDE_DIR.P endif; #required files -require_once(INC_DIR.'class.setup.php'); require_once(INCLUDE_DIR.'class.validator.php'); require_once(INCLUDE_DIR.'class.format.php'); require_once(INCLUDE_DIR.'class.misc.php'); require_once(INCLUDE_DIR.'mysql.php'); - ?> diff --git a/setup/test/lib/phplint.tcl b/setup/test/lib/phplint.tcl new file mode 100755 index 0000000000000000000000000000000000000000..6b857c75048a26fcd136d9213ece4c5b784bbfbc --- /dev/null +++ b/setup/test/lib/phplint.tcl @@ -0,0 +1,208 @@ +#!/usr/bin/tclsh + +# Copyright (C) 2007 Salvatore Sanfilippo <antirez at gmail dot com> +# This software is released under the GPL license version 2 + +proc scan file { + set fd [open $file] + set infunc 0 + set linenr 0 + set fnre {(^\s*)((public|private|protected|static)\s*)*function\s+([^(]+)\s*\((.*)\).*} + while {[gets $fd line] != -1} { + incr linenr + if {[regexp $fnre $line - ind - - - fa]} { + # If $infunc is true we miss the end of the last function + # so we analyze it now. + if {$infunc} { + analyze $file $arglist $body + } + set body {} + set arglist {} + foreach arg [split $fa ,] { + # remove default value + regsub {=.*} $arg {} arg + # remove optional type spec + regsub {^.*\s+} [string trim $arg] {} arg + set arg [string trim $arg " $&"] + lappend arglist $arg + } + set infunc 1 + } elseif {$infunc && [regexp "^$ind\}" $line]} { + set infunc 0 + analyze $file $arglist $body + } elseif {$infunc} { + lappend body $linenr [string trim $line] + } + } +} + +proc analyze {file arglist body} { + set initialized(this) 1 + set linton 1 + foreach arg $arglist { + set initialized($arg) 1 + } + # Superglobals + set superglobals { + "GLOBALS" + "_SESSION" + "_SESSION" + "_GET" + "_POST" + "_REQUEST" + "_ENV" + "_SERVER" + "_FILES" + "php_errormsg" + } + foreach sg $superglobals { + set initialized($sg) 1 + } + # analyze body + foreach {linenr line} $body { + # Handle annotations + if {[string first {nolint} [string tolower $line]] != -1} continue + if {[string first {linton} [string tolower $line]] != -1} { + if {$linton == 1} { + puts "! Warning 'linton' annotation with lint already ON" + continue + } + set linton 1 + puts ". $skipped lines skipped in $file from line $skipstart" + } + if {[string first {lintoff} [string tolower $line]] != -1} { + if {$linton == 0} { + puts "! Warning 'lintoff' annotation with lint already OFF" + continue + } + set linton 0 + set skipped 0 + set skipstart [expr {$linenr+1}] + continue + } + if {$linton == 0} { + incr skipped + continue + } + # Skip comments + if {[string index $line 0] eq {#}} continue + if {[string index $line 0] eq {/} && [string index $line 1] eq {/}} continue + # PHP variable regexp + set varre {\$[_A-Za-z]+[_A-Za-z0-9]*(\[[^\]]*\])*} + # Check for globals + set re {\s*(global|static)\s+((?:\$[^;,]+[ ,]*)+)(;|$)} + if {[regexp $re $line -> - g]} { + set g [split [string trim $g ";"] ,] + foreach v $g { + set v [string trim $v "$ "] + set initialized($v) 1 + } + } + # Check for assignment via foreach ... as &$varname + set re {} + append re {foreach\s*\(.*\s+as\s+&?(} $varre {)\s*\)} + set l [regexp -all -inline -nocase $re $line] + foreach {- a -} $l { + set initialized([string trim $a "$ "]) 1 + } + # Check for assignment via foreach ... as $key => &$val + set re {} + append re {foreach\s*\(.*\s+as\s+(} $varre {)\s*=>\s*&?(} $varre {)\s*\)} + set l [regexp -all -inline -nocase $re $line] + foreach {- a1 - a2 -} $l { + set initialized([string trim $a1 "$ "]) 1 + set initialized([string trim $a2 "$ "]) 1 + } + # Check for assigments in the form list($a,$b,$c) = ... + set re {list\s*\(([^=]*)\)\s*=} + set l [regexp -all -inline $re $line] + foreach {- vars} $l { + foreach v [split $vars ,] { + set v [string trim $v "$ "] + set initialized($v) 1 + } + } + # Check for assigments via = operator + set re $varre + append re {\s*=} + set l [regexp -all -inline $re $line] + foreach {a -} $l { + set a [string trim $a "=$ "] + regsub -all {\[.*\]} $a {[]} a + #puts "assigmnent of $a" + set initialized($a) 1 + regsub -all {\[\]} $a {} a + set initialized($a) 1 + } + # Check for assignments via catch(Exception $e) + set re {} + append re {catch\s*\(.*\s+(} $varre {)} + set l [regexp -all -inline -nocase $re $line] + foreach {- a} $l { + set initialized([string trim $a "$ "]) 1 + } + # Check for assignments by reference + # + # funclist format is {type funcname spos epos} where spos is the + # zero-based index of the first argument that can be considered + # an assignment, while epos is the last. + # + # name is the function name to match, and type is what + # to do with the args. "assignment" to consider them assigned + # or "ingore" to ingore them for the current line. + # + # The "ignore" is used for isset() and other functions that can + # deal with not initialized vars. + unset -nocomplain -- ignore + array set ignore {} + set funclist { + assignment scanf 2 100 + assignment preg_match 2 100 + assignment preg_match_all 2 100 + assignment ereg 2 100 + ignore isset 0 0 + } + set cline $line + regsub -all {'[^']+'} $cline {''} cline + foreach {type name spos epos} $funclist { + set re {} + append re $name {\s*\(([^()]*)\)} + foreach {- fargs} [regexp -all -inline $re $cline] { + set argidx 0 + foreach a [split $fargs ,] { + set a [string trim $a ", $"] + regsub -all {\[.*\]} $a {} a + if {$argidx >= $spos && $argidx <= $epos} { + if {$type eq {assignment}} { + set initialized($a) 1 + } elseif {$type eq {ignore}} { + set ignore($a) 1 + } + } + incr argidx + } + } + } + + # Check for var accesses + set varsimplere {\$[_A-Za-z]+[_A-Za-z0-9]*} + set l [regexp -all -inline $varsimplere $line] + foreach a $l { + set a [string trim $a "=$ "] + regsub -all {\[.*\]} $a {} a + #puts "access of $a" + if {![info exists initialized($a)] && + ![info exists ignore($a)]} { + puts "* In $file line $linenr: access to uninitialized var '$a'" + } + } + } +} + +proc main argv { + foreach file $argv { + scan $file + } +} + +main $argv diff --git a/setup/test/lint.php b/setup/test/lint.php new file mode 100644 index 0000000000000000000000000000000000000000..4566c495a247874f538fc043cd777185e17d509c --- /dev/null +++ b/setup/test/lint.php @@ -0,0 +1,107 @@ +#!/usr/bin/env php +<?php +if (php_sapi_name() != 'cli') exit(); + +function get_osticket_root_path() { + # Hop up to the root folder + $start = dirname(__file__); + for (;;) { + if (file_exists($start . '/main.inc.php')) break; + $start .= '/..'; + } + return realpath($start); +} + +$root = get_osticket_root_path(); + +# Check PHP syntax across all php files +function glob_recursive($pattern, $flags = 0) { + $files = glob($pattern, $flags); + foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { + $files = array_merge($files, + glob_recursive($dir.'/'.basename($pattern), $flags)); + } + return $files; +} +echo "PHP Syntax Errors: "; +ob_start(); +$scripts=glob_recursive("$root/*.php"); +$exit=0; +$syntax_errors=""; +foreach ($scripts as $s) { + system("php -l $s", $exit); + $line = ob_get_contents(); + ob_clean(); + if ($exit !== 0) + $syntax_errors .= $line; +} +ob_end_clean(); + +if (strlen($syntax_errors)) { + $syntax_errors=str_replace("$root/", '', $syntax_errors); + echo "FAIL\n"; + echo "-------------------------------------------------------\n"; + echo "$syntax_errors"; + exit(); +} else { + echo "\n"; +} + +# Run phplint across all php files +echo "Access to unitialized variables: "; +ob_start(); +# XXX: This won't run well on Windoze +system("$root/setup/test/lib/phplint.tcl ".implode(" ", $scripts)); +$lint_errors = ob_get_clean(); + +if (strlen($lint_errors)) { + $lint_errors=str_replace("$root/", '', $lint_errors); + echo "FAIL\n"; + echo "-------------------------------------------------------\n"; + echo "$lint_errors"; +} else { + echo "\n"; +} + +function find_function_calls($scripts) { + $calls=array(); + foreach ($scripts as $s) { + $lines = explode("\n", file_get_contents($s)); + $lineno=0; + foreach (explode("\n", file_get_contents($s)) as $line) { + $lineno++; $matches=array(); + preg_match_all('/-[>]([a-zA-Z0-9]*)\(/', $line, $matches, + PREG_SET_ORDER); + foreach ($matches as $m) { + $calls[] = array($s, $lineno, $line, $m[1]); + } + } + } + return $calls; +} + +$php_script_content=''; +foreach ($scripts as $s) { + $php_script_content .= file_get_contents($s); +} +echo "Access to undefined object methods: "; +ob_start(); +foreach (find_function_calls($scripts) as $call) { + list($file, $no, $line, $func) = $call; + if (!preg_match('/^\s*(\/\*[^*]*\*\/)?'."\s*function\s+&?\s*$func\\(/m", + $php_script_content)) { + print "$func: Definitely undefined, from $file:$no\n"; + } +} +$undef_func_errors = ob_get_clean(); + +if (strlen($undef_func_errors)) { + $undef_func_errors=str_replace("$root/", '', $undef_func_errors); + echo "FAIL\n"; + echo "-------------------------------------------------------\n"; + echo "$undef_func_errors"; + exit(); +} else { + echo "\n"; +} +?> diff --git a/setup/upgrade.php b/setup/upgrade.php index 6fc34cfd307cfcc9df319deb25101ec159f52d75..0406383707cfe245fd11aae9323ee1422d6d679e 100644 --- a/setup/upgrade.php +++ b/setup/upgrade.php @@ -13,5 +13,89 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ -die('Upgrade NOT supported for v1.7 DPR.'); +function staffLoginPage($msg) { + + $_SESSION['_staff']['auth']['dest']=THISPAGE; + $_SESSION['_staff']['auth']['msg']=$msg; + header('Location: ../scp/login.php'); + exit; +} + +require '../scp/staff.inc.php'; +if(!$thisstaff or !$thisstaff->isAdmin()) { + staffLoginPage('Admin Access Required!'); + exit; +} + +define('SETUPINC', true); +define('INC_DIR', './inc/'); +define('SQL_DIR', INC_DIR.'sql/'); + +require_once INC_DIR.'class.upgrader.php'; + +//$_SESSION['ost_upgrader']=null; +$upgrader = new Upgrader($cfg->getSchemaSignature(), TABLE_PREFIX, SQL_DIR); + + +$wizard=array(); +$wizard['title']='osTicket Upgrade Wizard'; +$wizard['tagline']='Upgrading osTicket to v'.$upgrader->getVersionVerbose(); +$wizard['logo']='logo-upgrade.png'; +$wizard['menu']=array('Upgrade Guide'=>'http://osticket.com/wiki/Upgrade_and_Migration', + 'Get Professional Help'=>'http://osticket.com/support'); +$errors=array(); +if($_POST && $_POST['s'] && !$upgrader->isAborted()) { + switch(strtolower($_POST['s'])) { + case 'prereq': + //XXX: check if it's upgradable version?? + if(!$cfg->isUpgradePending()) + $errors['err']=' Nothing to do! System already upgraded to the current version'; + elseif(!$upgrader->isUpgradable()) + $errors['err']='The upgrader does NOT support upgrading from the current vesion!'; + elseif($upgrader->check_prereq()) + $upgrader->setState('upgrade'); + else + $errors['prereq']='Minimum requirements not met!'; + break; + case 'upgrade': //Manual upgrade.... when JS (ajax) is not supported. + if($upgrader->getNumPendingTasks()) { + $upgrader->doTasks(); + } elseif($cfg->isUpgradePending() && $upgrader->isUpgradable()) { + $upgrader->upgrade(); + } elseif(!$cfg->isUpgradePending()) { + $upgrader->setState('done'); + } + + if(($errors=$upgrader->getErrors())) { + $upgrader->setState('aborted'); + } + break; + default: + $errors['err']='Unknown action!'; + } +} + +switch(strtolower($upgrader->getState())) { + case 'aborted': + $inc='upgrade-aborted.inc.php'; + break; + case 'upgrade': + $inc='upgrade.inc.php'; + break; + case 'done': + $inc='upgrade-done.inc.php'; + break; + default: + $inc='upgrade-prereq.inc.php'; + if($upgrader->isAborted()) + $inc='upgrade-aborted.inc.php'; + elseif(!$cfg->isUpgradePending()) + $errors['err']='Nothing to do! System already upgraded to the latest version'; + elseif(!$upgrader->isUpgradable()) + $errors['err']='The upgrader does NOT support upgrading from the current vesion!'; +} + +require(INC_DIR.'header.inc.php'); +require(INC_DIR.$inc); +require(INC_DIR.'footer.inc.php'); ?>