diff --git a/.gitignore b/.gitignore index 2c0568a588f2c2759a62bc94151c0597f8c09abe..b5c98c948d9c2eb75df122268b315b29f15a9e93 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ php53.cgi include/ost-config.php *.sw[a-z] .DS_Store +.vagrant +Vagrantfile diff --git a/assets/default/about-custom-themes.md b/assets/default/about-custom-themes.md new file mode 100644 index 0000000000000000000000000000000000000000..eecf1bbbed3d3002d3d4abe686f8ec6d82717094 --- /dev/null +++ b/assets/default/about-custom-themes.md @@ -0,0 +1,14 @@ +Customizng Your Theme +==== + +When modifying the default theme, it is recommended that you do not +edit the CSS files directly. Instead, you should use the included +LESS files and recompile the CSS file with your edits. + +Even if you decide to edit the CSS directly, we recommend that you +keep the LESS files, so that you can quickly rebuild the default +theme should you need to recover it, or for upgrade purposes. + +*Please do not submit any CSS edits to the official branch, unless +they are done within the LESS files.* + diff --git a/assets/default/css/print.css b/assets/default/css/print.css index 9f1c25746ca0486d2524e24ea850b1d2ffcd9e6c..aca3d50f9265e03c3a95d5a5dbbe78aa891b0a21 100644 --- a/assets/default/css/print.css +++ b/assets/default/css/print.css @@ -1,29 +1 @@ -#header, #nav, #meta, #footer, #reply, #pagination, .reload, .refresh, form, .thread, hr, #kbAttachments, .back { - display: none; -} - -th { - text-align: left; -} - -a { - color: #000; - text-decoration: none; -} - -caption { - text-align: left; - padding-bottom: 10px; - font-weight: bold; -} - -.message, .response { - border-bottom: 1px solid #000; - margin-bottom: 20px; - padding-bottom: 10px; -} -.message th, .response th { - font-size: 12pt; - font-weight: bold; - padding-bottom: 5px; -} +#header,#nav,#meta,#footer,#reply,#pagination,.reload,.refresh,form,.thread,hr,#kbAttachments,.back{display:none}th{text-align:left}a{color:#000;text-decoration:none}caption{text-align:left;padding-bottom:10px;font-weight:bold}.message,.response{border-bottom:1px solid #000;margin-bottom:20px;padding-bottom:10px}.message th,.response th{font-size:12pt;font-weight:bold;padding-bottom:5px} \ No newline at end of file diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css index b59f6af8955857d2008dff647e8e4d83d4924e72..206cbe9fd0eb86d67fa1d16abae23e9a49aab9ca 100644 --- a/assets/default/css/theme.css +++ b/assets/default/css/theme.css @@ -1,31 +1,29 @@ -/* Based on Normalize.css - with tags we won't use removed. */ html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } - body { margin: 0; font-size: 13px; line-height: 1.231; padding: 0; } - -body, input, select, textarea { +body, +input, +select, +textarea { font-family: sans-serif; color: #000; } - -b, strong { +b, +strong { font-weight: bold; } - blockquote { margin: 1em 40px; } - hr { display: block; height: 1px; @@ -34,119 +32,120 @@ hr { margin: 1em 0; padding: 0; } - small { font-size: 85%; } - -ul, ol { +ul, +ol { margin: 1em 0; padding: 0 0 0 30px; } - img { border: 0; vertical-align: middle; } - form { margin: 0; } - fieldset { border: 0; margin: 0; padding: 0; } - label { cursor: pointer; } - -input, select, textarea { +input, +select, +textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } - input { line-height: normal; *overflow: visible; } - table input { *overflow: auto; } - -input[type="button"], input[type="reset"], input[type="submit"] { +input[type="button"], +input[type="reset"], +input[type="submit"] { cursor: pointer; -webkit-appearance: button; } - -input[type="checkbox"], input[type="radio"] { +input[type="checkbox"], +input[type="radio"] { box-sizing: border-box; } - input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } - textarea { overflow: auto; vertical-align: top; resize: vertical; } - table { border-collapse: collapse; border-spacing: 0; } - -th, td { +th, +td { vertical-align: top; } - -th { text-align: left; font-weight: normal; } - -h1, h2, h3, h4, h5, h6, form, fieldset { +th { + text-align: left; + font-weight: normal; +} +h1, +h2, +h3, +h4, +h5, +h6, +form, +fieldset { margin: 0; padding: 0; } - /* Typography */ a { color: #0072bc; text-decoration: none; } - h1 { color: #00AEEF; font-weight: normal; font-size: 20px; } - h3 { font-size: 16px; } - h2 { font-size: 16px; color: #999; } - /* Helpers */ -.centered { text-align: center;} - -.clear { clear:both; height: 1px; visibility: none;} - -.hidden { display: none;} - -.faded { color:#666;} - +.centered { + text-align: center; +} +.clear { + clear: both; + height: 1px; + visibility: none; +} +.hidden { + display: none; +} +.faded { + color: #666; +} /* Pagination */ #pagination { border: 0; @@ -161,24 +160,26 @@ h2 { list-style: none; display: inline; } -#pagination a { +#pagination li a { margin-right: 2px; display: block; float: left; padding: 3px 6px; text-decoration: none; } -#pagination a:hover { +#pagination li a:hover { color: #ff0084; } -#pagination .previousOff, #pagination .nextOff { +#pagination .previousOff, +#pagination .nextOff { color: #666; display: block; float: left; font-weight: bold; padding: 3px 4px; } -#pagination .next a, #pagination .previous a { +#pagination .next a, +#pagination .previous a { font-weight: bold; } #pagination .active { @@ -190,16 +191,34 @@ h2 { padding: 3px 6px; text-decoration: none; } - /* Alerts & Notices */ - -#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') 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') 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; } - - +#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') 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') 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; +} .warning { background: #ffc; font-style: italic; @@ -209,19 +228,64 @@ h2 { color: #a00; font-style: normal; } - .error { - color:#f00; + color: #f00; } - .error input { - border:1px solid #f00;} - -/* Main layout */ + border: 1px solid #f00; +} +.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; +} body { background: url('../images/page_bg.png') top left repeat-x #c8c8c8; } - #container { background: #fff; width: 840px; @@ -230,7 +294,6 @@ body { -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); } - #header { position: relative; height: 71px; @@ -241,7 +304,6 @@ body { height: 71px; float: left; } - #header p { width: 400px; text-align: right; @@ -249,16 +311,15 @@ body { padding: 10px 0; float: right; } - #nav { margin: 0 20px; padding: 2px 10px; 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); + 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; @@ -282,7 +343,8 @@ body { background-position: 10px 50%; background-repeat: no-repeat; } -#nav li a.active, #nav li a:hover { +#nav li a.active, +#nav li a:hover { background-color: #dbefff; color: #000; } @@ -305,7 +367,6 @@ body { #nav li a.tickets { background-image: url('../images/icons/tix.png'); } - #content { padding: 20px 0; margin: 0 20px; @@ -313,7 +374,6 @@ body { height: 350px; min-height: 350px; } - #footer { text-align: center; font-size: 11px; @@ -322,7 +382,6 @@ body { #footer a { color: #333; } - #footer p { margin: 10px 0 0 0; } @@ -333,45 +392,23 @@ body { outline: none; text-indent: -9999px; margin: 0 auto; - background: url('../images/poweredby.png?1319571688') top left no-repeat; + background: url('../images/poweredby.png') top left no-repeat; } - -/* Landing page */ #landing_page #new_ticket { margin-top: 40px; width: 295px; padding-left: 75px; float: left; - background: url('../images/new_ticket_icon.png?1319577021') top left no-repeat; -} -#landing_page #new_ticket input[type=submit] { - background: url('../images/open_ticket_btn.png?1319566422') top left no-repeat; + background: url('../images/new_ticket_icon.png') top left no-repeat; } #landing_page #check_status { margin-top: 40px; width: 295px; padding-left: 75px; float: right; - background: url('../images/check_status_icon.png?1319577072') top left no-repeat; -} -#landing_page #check_status input[type=submit] { - background: url('../images/check_status_btn.png?1319571066') top left no-repeat; + background: url('../images/check_status_icon.png') top left no-repeat; } -#landing_page form div { - margin-bottom: 20px; - height: 60px; - min-height: 60px; -} -#landing_page form input[type=submit] { - display: block; - width: 192px; - height: 38px; - border: none; - margin: 0; - padding: 0; - text-indent: -9999px; -} - +/* Landing page FAQ not yet implemented. */ #faq { clear: both; margin: 0; @@ -381,136 +418,129 @@ body { font-size: 15px; margin-left: 0; padding-left: 0; - border-top:1px solid #ddd; + border-top: 1px solid #ddd; } #faq ol li { list-style: none; margin: 0; - padding:0; + padding: 0; color: #999; } #faq ol li a { - display:block; - padding:5px 0; - height:auto !important; - overflow:hidden; - margin:0; - border-bottom:1px solid #ddd; + 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; + background: url('../images/icons/page.png') 0 50% no-repeat; } #faq ol li a:hover { - background-color:#e9f5ff; + background-color: #e9f5ff; } - -.article-meta { - padding:5px; - background:#fafafa; +#faq .article-meta { + padding: 5px; + background: #fafafa; } - -/* Knowledgebase */ #kb { margin: 2px 0; padding: 5px; overflow: hidden; } - #kb > li { - 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; -} - + 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 h4 { - padding-bottom:3px; - margin-bottom:3px; + padding-bottom: 3px; + margin-bottom: 3px; } - #kb > li h4 span { - color:#666; + color: #666; } - #kb > li h4 a { font-size: 14px; } - +#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-search { - padding:10px 0; - overflow:hidden; + padding: 10px 0; + overflow: hidden; } - #kb-search div { - clear:both; - overflow:hidden; - padding-top:5px; + clear: both; + overflow: hidden; + padding-top: 5px; } - #kb-search #query { - margin:0; - display:inline-block; - float:left; - width:200px; - margin-right:5px; + 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; -} - + 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; + margin: 0; + display: inline-block; + float: left; + width: 410px; } - #kb-search #searchSubmit { - margin:0; - display:inline-block; - float:left; - position:relative; - top:2px; -} - -#breadcrumbs { + margin: 0; + display: inline-block; + float: left; + position: relative; + top: 2px; +} +#kb-search #breadcrumbs { color: #333; margin-bottom: 15px; } -#breadcrumbs a { +#kb-search #breadcrumbs #breadcrumbs a { color: #555; } - -/* New Ticket & Log In Forms */ -#ticketForm div, #clientLogin div { +#ticketForm div, +#clientLogin div { clear: both; padding: 3px 0; overflow: hidden; } -#ticketForm div label, #clientLogin div label { +#ticketForm div label, +#clientLogin div label { display: block; width: 140px; float: left; } -#ticketForm div input, #ticketForm div textarea, #clientLogin div input, #clientLogin div textarea { +#ticketForm div label.required, +#clientLogin div label.required { + font-weight: bold; + text-align: left; +} +#ticketForm div input, +#clientLogin div input, +#ticketForm div textarea, +#clientLogin div textarea { width: auto; border: 1px solid #aaa; background: #fff; @@ -518,23 +548,40 @@ body { display: block; float: left; } - -#ticketForm div input[type=file] { +#ticketForm div input[type=file], +#clientLogin div input[type=file] { border: 0; } - -#ticketForm div select, #clientLogin div select { +#ticketForm div select, +#clientLogin div select { display: block; float: left; } -#ticketForm td textarea, #clientLogin div textarea { +#ticketForm div div.captchaRow, +#clientLogin div div.captchaRow { + line-height: 31px; +} +#ticketForm div div.captchaRow input, +#clientLogin div div.captchaRow input { + position: relative; + top: 6px; +} +#ticketForm td textarea, +#clientLogin td textarea, +#ticketForm div textarea, +#clientLogin div textarea { width: 600px; } - -#ticketForm td em, #clientLogin div em { +#ticketForm td em, +#clientLogin td em, +#ticketForm div em, +#clientLogin div em { color: #777; } -#ticketForm td .captcha, #clientLogin div .captcha { +#ticketForm td .captcha, +#clientLogin td .captcha, +#ticketForm div .captcha, +#clientLogin div .captcha { width: 88px; height: 31px; background: #000; @@ -542,44 +589,31 @@ body { float: left; margin-right: 20px; } -#ticketForm td label.inline, #clientLogin div label.inline { +#ticketForm td label.inline, +#clientLogin td label.inline, +#ticketForm div label.inline, +#clientLogin div label.inline { width: auto; padding: 0 10px; } - -#ticketTable table tr th { +#ticketForm div.error input, +#clientLogin div.error input { + border: 1px solid #a00; +} +#ticketForm div.error label, +#clientLogin div.error label { + color: #a00; +} +#ticketTable th { width: 160px; font-weight: normal; text-align: left; } - -#ticketForm table th.required, #ticketForm table td.required, #clientLogin div label.required { +#ticketTable th.required, +#ticketTable td.required { font-weight: bold; text-align: left; } - - - -#ticketForm tr.captchaRow, #clientLogin div.captchaRow { - line-height: 31px; -} - -.captchaRow td input, #clientLogin div.captchaRow input { - position: relative; - top: 6px; -} - -#ticketForm div.error input, #clientLogin div.error input { - border: 1px solid #a00; -} -#ticketForm div.error label, #clientLogin div.error label { - color: #a00; -} -#clientLogin p { - clear: both; - text-align: center; -} - #clientLogin { width: 400px; margin-top: 20px; @@ -587,6 +621,10 @@ body { border: 1px solid #ccc; background: url('../images/lock.png?1319655200') 440px 50% no-repeat #f6f6f6; } +#clientLogin p { + clear: both; + text-align: center; +} #clientLogin strong { font-size: 11px; color: #d00; @@ -601,59 +639,89 @@ body { width: 120px; margin-right: 0; } - -/* Ticket List */ +#reply { + margin-top: 20px; + padding: 10px 5px; + background: #f9f9f9; + border: 1px solid #ccc; +} +#reply h2 { + margin-bottom: 10px; +} +#reply table { + width: 800px; +} +#reply table td { + vertical-align: top; +} +#reply textarea { + width: 628px !important; +} +#reply input[type=text], +#reply #response_options textarea { + border: 1px solid #aaa; + background: #fff; +} +#reply .attachments .uploads div { + display: inline-block; + padding-right: 20px; +} +#reply .file { + display: inline-block; + padding-left: 20px; + margin-right: 20px; + background: url('../images/icons/file.gif') 0 50% no-repeat; +} +.uploads { + display: inline-block; + padding-right: 20px; +} +.uploads label { + padding: 3px; + padding-right: 10px; + width: auto !important; +} +/* Ticket icons */ .Icon { width: auto; padding-left: 20px; - background-position: left center; + background-position: top left; background-repeat: no-repeat; color: #006699; text-decoration: none; } - -a.Icon:hover { - text-decoration: underline; -} - .Icon.Ticket { - background: url('../images/icons/ticket.gif?1319654018') 0 0 no-repeat; + background-image: url('../images/icons/ticket.gif'); } - .Icon.webTicket { - background: url('../images/icons/ticket_source_web.gif?1319654283') 0 0 no-repeat; + background-image: url('../images/icons/ticket_source_web.gif'); } - .Icon.emailTicket { - background: url('../images/icons/ticket_source_email.gif?1319654484') 0 0 no-repeat; + background-image: url('../images/icons/ticket_source_email.gif'); } - .Icon.phoneTicket { - background: url('../images/icons/ticket_source_phone.gif?1319654401') 0 0 no-repeat; + background-image: url('../images/icons/ticket_source_phone.gif'); } - .Icon.otherTicket { - background: url('../images/icons/ticket_source_other.gif?1319654433') 0 0 no-repeat; + background-image: url('../images/icons/ticket_source_other.gif'); } - .Icon.attachment { - background-image: url('../images/icons/attachment.gif?1319556657'); + background-image: url('../images/icons/attachment.gif'); } - .Icon.file { - background-image: url('../images/icons/attachment.gif?1319556657'); + background-image: url('../images/icons/attachment.gif'); } - .Icon.refresh { - background-image: url('../images/icons/refresh.gif?1319556657'); + background-image: url('../images/icons/refresh.gif'); } - .Icon.thread { font-weight: bold; font-size: 1em; background-image: url('../images/icons/thread.gif?1319556657'); } - +.Icon:hover { + text-decoration: underline; +} #ticketTable { border: 1px solid #aaa; border-left: none; @@ -688,13 +756,11 @@ a.Icon:hover { #ticketTable tr.alt td { background: #f9f9f9; } - #ticketSearchForm { display: inline-block; float: left; padding: 0 0 5px 0; } - a.refresh { display: block; width: auto; @@ -708,16 +774,14 @@ a.refresh { color: #333; background-position: 5px 50%; background-repeat: no-repeat; - background-image: url('../images/icons/refresh.png?1319653435'); + background-image: url('../images/icons/refresh.png'); } - .infoTable { background: #F4FAFF; } .infoTable th { text-align: left; } - #ticketThread table { margin-top: 10px; border: 1px solid #aaa; @@ -729,13 +793,11 @@ a.refresh { font-size: 12px; padding: 5px; } - #ticketThread table th span { - font-weight:normal; - color:#888; - padding-left:20px; + font-weight: normal; + color: #888; + padding-left: 20px; } - #ticketThread table td { padding: 5px; } @@ -761,76 +823,3 @@ a.refresh { background-position: 0 50%; background-repeat: no-repeat; } - -#reply { - margin-top: 20px; - padding: 10px 5px; - background: #f9f9f9; - border: 1px solid #ccc; -} -#reply h2 { - margin-bottom: 10px; -} -#reply table { - width: 800px; -} -#reply table td { - vertical-align: top; -} -#reply textarea { - width: 628px !important; -} -#reply input[type=text], #reply #response_options textarea { - border: 1px solid #aaa; - background: #fff; -} -#reply .attachments .uploads div { - display: inline-block; - padding-right: 20px; -} -#reply .file { - display: inline-block; - padding-left: 20px; - 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; -} - - -.uploads { - display:inline-block; - padding-right:20px; -} - -.uploads label { padding:3px; padding-right:10px; width: auto !important } - -.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/css/theme.min.css b/assets/default/css/theme.min.css new file mode 100644 index 0000000000000000000000000000000000000000..8e747a44528fe4376371c967fd867ec835f01fce --- /dev/null +++ b/assets/default/css/theme.min.css @@ -0,0 +1 @@ +html{font-size:100%;overflow-y:scroll;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0;font-size:13px;line-height:1.231;padding:0}body,input,select,textarea{font-family:sans-serif;color:#000}b,strong{font-weight:bold}blockquote{margin:1em 40px}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}small{font-size:85%}ul,ol{margin:1em 0;padding:0 0 0 30px}img{border:0;vertical-align:middle}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}input{line-height:normal;*overflow:visible}table input{*overflow:auto}input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="checkbox"],input[type="radio"]{box-sizing:border-box}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}th,td{vertical-align:top}th{text-align:left;font-weight:normal}h1,h2,h3,h4,h5,h6,form,fieldset{margin:0;padding:0}a{color:#0072bc;text-decoration:none}h1{color:#00aeef;font-weight:normal;font-size:20px}h3{font-size:16px}h2{font-size:16px;color:#999}.centered{text-align:center}.clear{clear:both;height:1px;visibility:none}.hidden{display:none}.faded{color:#666}#pagination{border:0;margin:0 0 40px 0;padding:0}#pagination li{border:0;margin:0;padding:0;font-size:11px;list-style:none;display:inline}#pagination li a{margin-right:2px;display:block;float:left;padding:3px 6px;text-decoration:none}#pagination li a:hover{color:#ff0084}#pagination .previousOff,#pagination .nextOff{color:#666;display:block;float:left;font-weight:bold;padding:3px 4px}#pagination .next a,#pagination .previous a{font-weight:bold}#pagination .active{color:#000;font-weight:bold;margin-right:2px;display:block;float:left;padding:3px 6px;text-decoration:none}#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') 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') 10px 50% no-repeat #ffd}#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}.warning{background:#ffc;font-style:italic}.warning strong{text-transform:uppercase;color:#a00;font-style:normal}.error{color:#f00}.error input{border:1px solid #f00}.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}body{background:url('../images/page_bg.png') top left repeat-x #c8c8c8}#container{background:#fff;width:840px;margin:0 auto;box-shadow:0 0 6px rgba(0,0,0,0.5);-moz-box-shadow:0 0 6px rgba(0,0,0,0.5);-webkit-box-shadow:0 0 6px rgba(0,0,0,0.5)}#header{position:relative;height:71px;padding:0 20px}#header #logo{width:220px;height:71px;float:left}#header p{width:400px;text-align:right;margin:0;padding:10px 0;float:right}#nav{margin:0 20px;padding:2px 10px;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;padding:0;list-style:none;display:inline}#nav li a{display:block;width:auto;float:left;height:20px;line-height:20px;text-align:center;padding:0 10px 0 32px;margin-left:10px;color:#333;border-radius:20px;-webkit-border-radius:20px;-moz-border-radius:20px;background-position:10px 50%;background-repeat:no-repeat}#nav li a.active,#nav li a:hover{background-color:#dbefff;color:#000}#nav li a:hover{background-color:#ededed;color:#0054a6}#nav li a.home{background-image:url('../images/icons/home.png')}#nav li a.kb{background-image:url('../images/icons/kb.png')}#nav li a.new{background-image:url('../images/icons/new.png')}#nav li a.status{background-image:url('../images/icons/status.png')}#nav li a.tickets{background-image:url('../images/icons/tix.png')}#content{padding:20px 0;margin:0 20px;height:auto!important;height:350px;min-height:350px}#footer{text-align:center;font-size:11px;color:#333}#footer a{color:#333}#footer p{margin:10px 0 0 0}#footer #poweredBy{display:block;width:126px;height:23px;outline:0;text-indent:-9999px;margin:0 auto;background:url('../images/poweredby.png') top left no-repeat}#landing_page #new_ticket{margin-top:40px;width:295px;padding-left:75px;float:left;background:url('../images/new_ticket_icon.png') top left no-repeat}#landing_page #check_status{margin-top:40px;width:295px;padding-left:75px;float:right;background:url('../images/check_status_icon.png') top left no-repeat}#faq{clear:both;margin:0;padding:5px}#faq ol{font-size:15px;margin-left:0;padding-left:0;border-top:1px solid #ddd}#faq ol li{list-style:none;margin:0;padding:0;color:#999}#faq ol li a{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') 0 50% no-repeat}#faq ol li a:hover{background-color:#e9f5ff}#faq .article-meta{padding:5px;background:#fafafa}#kb{margin:2px 0;padding:5px;overflow:hidden}#kb>li{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 h4{padding-bottom:3px;margin-bottom:3px}#kb>li h4 span{color:#666}#kb>li h4 a{font-size:14px}#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-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}#kb-search #breadcrumbs{color:#333;margin-bottom:15px}#kb-search #breadcrumbs #breadcrumbs a{color:#555}#ticketForm div,#clientLogin div{clear:both;padding:3px 0;overflow:hidden}#ticketForm div label,#clientLogin div label{display:block;width:140px;float:left}#ticketForm div label.required,#clientLogin div label.required{font-weight:bold;text-align:left}#ticketForm div input,#clientLogin div input,#ticketForm div textarea,#clientLogin div textarea{width:auto;border:1px solid #aaa;background:#fff;margin-right:10px;display:block;float:left}#ticketForm div input[type=file],#clientLogin div input[type=file]{border:0}#ticketForm div select,#clientLogin div select{display:block;float:left}#ticketForm div div.captchaRow,#clientLogin div div.captchaRow{line-height:31px}#ticketForm div div.captchaRow input,#clientLogin div div.captchaRow input{position:relative;top:6px}#ticketForm td textarea,#clientLogin td textarea,#ticketForm div textarea,#clientLogin div textarea{width:600px}#ticketForm td em,#clientLogin td em,#ticketForm div em,#clientLogin div em{color:#777}#ticketForm td .captcha,#clientLogin td .captcha,#ticketForm div .captcha,#clientLogin div .captcha{width:88px;height:31px;background:#000;display:block;float:left;margin-right:20px}#ticketForm td label.inline,#clientLogin td label.inline,#ticketForm div label.inline,#clientLogin div label.inline{width:auto;padding:0 10px}#ticketForm div.error input,#clientLogin div.error input{border:1px solid #a00}#ticketForm div.error label,#clientLogin div.error label{color:#a00}#ticketTable th{width:160px;font-weight:normal;text-align:left}#ticketTable th.required,#ticketTable td.required{font-weight:bold;text-align:left}#clientLogin{width:400px;margin-top:20px;padding:10px 100px 10px 10px;border:1px solid #ccc;background:url('../images/lock.png?1319655200') 440px 50% no-repeat #f6f6f6}#clientLogin p{clear:both;text-align:center}#clientLogin strong{font-size:11px;color:#d00;display:block;padding-left:140px}#clientLogin #email{width:250px;margin-right:0}#clientLogin #ticketno{width:120px;margin-right:0}#reply{margin-top:20px;padding:10px 5px;background:#f9f9f9;border:1px solid #ccc}#reply h2{margin-bottom:10px}#reply table{width:800px}#reply table td{vertical-align:top}#reply textarea{width:628px!important}#reply input[type=text],#reply #response_options textarea{border:1px solid #aaa;background:#fff}#reply .attachments .uploads div{display:inline-block;padding-right:20px}#reply .file{display:inline-block;padding-left:20px;margin-right:20px;background:url('../images/icons/file.gif') 0 50% no-repeat}.uploads{display:inline-block;padding-right:20px}.uploads label{padding:3px;padding-right:10px;width:auto!important}.Icon{width:auto;padding-left:20px;background-position:top left;background-repeat:no-repeat;color:#069;text-decoration:none}.Icon.Ticket{background-image:url('../images/icons/ticket.gif')}.Icon.webTicket{background-image:url('../images/icons/ticket_source_web.gif')}.Icon.emailTicket{background-image:url('../images/icons/ticket_source_email.gif')}.Icon.phoneTicket{background-image:url('../images/icons/ticket_source_phone.gif')}.Icon.otherTicket{background-image:url('../images/icons/ticket_source_other.gif')}.Icon.attachment{background-image:url('../images/icons/attachment.gif')}.Icon.file{background-image:url('../images/icons/attachment.gif')}.Icon.refresh{background-image:url('../images/icons/refresh.gif')}.Icon.thread{font-weight:bold;font-size:1em;background-image:url('../images/icons/thread.gif?1319556657')}.Icon:hover{text-decoration:underline}#ticketTable{border:1px solid #aaa;border-left:none;border-bottom:0}#ticketTable caption{padding:5px;text-align:left;color:#000;background:#ddd;border:1px solid #aaa;border-bottom:0;font-weight:bold}#ticketTable th{height:24px;line-height:24px;background:#e1f2ff;border:1px solid #aaa;border-right:0;border-top:0}#ticketTable th a{color:#000}#ticketTable td{padding:2px;border:1px solid #aaa;border-right:0;border-top:0}#ticketTable tr.alt td{background:#f9f9f9}#ticketSearchForm{display:inline-block;float:left;padding:0 0 5px 0}a.refresh{display:block;width:auto;float:right;height:20px;line-height:20px;text-align:center;padding:0 10px 0 28px;border:1px solid #aaa;margin-left:10px;color:#333;background-position:5px 50%;background-repeat:no-repeat;background-image:url('../images/icons/refresh.png')}.infoTable{background:#f4faff}.infoTable th{text-align:left}#ticketThread table{margin-top:10px;border:1px solid #aaa;border-bottom:2px solid #aaa}#ticketThread table th{text-align:left;border-bottom:1px solid #aaa;font-size:12px;padding:5px}#ticketThread table th span{font-weight:normal;color:#888;padding-left:20px}#ticketThread table td{padding:5px}#ticketThread .message th{background:#d8efff}#ticketThread .response th{background:#ddd}#ticketThread .info{padding:2px;background:#f9f9f9;border-top:1px solid #ddd;height:16px;line-height:16px}#ticketThread .info a{display:inline-block;margin:5px 10px 5px 0;padding-left:24px;height:16px;line-height:16px;background-position:0 50%;background-repeat:no-repeat} \ No newline at end of file diff --git a/assets/default/less/base.less b/assets/default/less/base.less new file mode 100644 index 0000000000000000000000000000000000000000..b6b4681574e7f11178100467eca70ab2ef0fa266 --- /dev/null +++ b/assets/default/less/base.less @@ -0,0 +1,131 @@ +/* Typography */ +a { + color: #0072bc; + text-decoration: none; +} + +h1 { + color: #00AEEF; + font-weight: normal; + font-size: 20px; +} + +h3 { + font-size: 16px; +} + +h2 { + font-size: 16px; + color: #999; +} + +/* Helpers */ +.centered { text-align: center;} + +.clear { clear:both; height: 1px; visibility: none;} + +.hidden { display: none;} + +.faded { color:#666;} + +/* Pagination */ +#pagination { + border: 0; + margin: 0 0 40px 0; + padding: 0; + li { + border: 0; + margin: 0; + padding: 0; + font-size: 11px; + list-style: none; + display: inline; + a { + margin-right: 2px; + display: block; + float: left; + padding: 3px 6px; + text-decoration: none; + } + a:hover { + color: #ff0084; + } + } + .previousOff, .nextOff { + color: #666; + display: block; + float: left; + font-weight: bold; + padding: 3px 4px; + } + .next a, .previous a { + font-weight: bold; + } + .active { + color: #000; + font-weight: bold; + margin-right: 2px; + display: block; + float: left; + padding: 3px 6px; + text-decoration: none; + } +} + +/* Alerts & Notices */ + +#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') 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') 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; } + + +.warning { + background: #ffc; + font-style: italic; + strong { + text-transform: uppercase; + color: #a00; + font-style: normal; + } +} + +.error { + color:#f00; + input { + border:1px solid #f00; + } +} + + +.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/less/kb.less b/assets/default/less/kb.less new file mode 100644 index 0000000000000000000000000000000000000000..f31faa4d0b99d4decd83dfda5dff3adad24b5bba --- /dev/null +++ b/assets/default/less/kb.less @@ -0,0 +1,88 @@ +#kb { + margin: 2px 0; + padding: 5px; + overflow: hidden; + + > li { + 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; + h4 { + padding-bottom:3px; + margin-bottom:3px; + span { + color:#666; + } + a { + font-size: 14px; + } + } + } + + 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-search { + padding:10px 0; + overflow:hidden; + + div { + clear:both; + overflow:hidden; + padding-top:5px; + } + + #query { + margin:0; + display:inline-block; + float:left; + width:200px; + margin-right:5px; + } + + #cid { + margin:0; + display:inline-block; + float:left; + width:200px; + margin-right:5px; + position:relative; + top:2px; + } + + #topic-id { + margin:0; + display:inline-block; + float:left; + width:410px; + } + + #searchSubmit { + margin:0; + display:inline-block; + float:left; + position:relative; + top:2px; + } + + #breadcrumbs { + color: #333; + margin-bottom: 15px; + + #breadcrumbs a { + color: #555; + } + } +} diff --git a/assets/default/less/landing-page.less b/assets/default/less/landing-page.less new file mode 100644 index 0000000000000000000000000000000000000000..cfcfcfcbf63ff4b7295372dc49b5124043e80d01 --- /dev/null +++ b/assets/default/less/landing-page.less @@ -0,0 +1,58 @@ +#landing_page { + #new_ticket { + margin-top: 40px; + width: 295px; + padding-left: 75px; + float: left; + background: url('../images/new_ticket_icon.png') top left no-repeat; + } + + #check_status { + margin-top: 40px; + width: 295px; + padding-left: 75px; + float: right; + background: url('../images/check_status_icon.png') top left no-repeat; + } +} + +/* Landing page FAQ not yet implemented. */ +#faq { + clear: both; + margin: 0; + padding: 5px; + + ol { + font-size: 15px; + margin-left: 0; + padding-left: 0; + border-top:1px solid #ddd; + + li { + list-style: none; + margin: 0; + padding:0; + color: #999; + a { + 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') 0 50% no-repeat; + } + + a:hover { + background-color:#e9f5ff; + } + } + } + .article-meta { + padding:5px; + background:#fafafa; + } +} + diff --git a/assets/default/less/main-layout.less b/assets/default/less/main-layout.less new file mode 100644 index 0000000000000000000000000000000000000000..a40cac090aad2fa4f287fbfa8955d0da04f246de --- /dev/null +++ b/assets/default/less/main-layout.less @@ -0,0 +1,114 @@ +body { + background: url('../images/page_bg.png') top left repeat-x #c8c8c8; +} + +#container { + background: #fff; + width: 840px; + margin: 0 auto; + box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); +} + +#header { + position: relative; + height: 71px; + padding: 0 20px; + + #logo { + width: 220px; + height: 71px; + float: left; + } + + p { + width: 400px; + text-align: right; + margin: 0; + padding: 10px 0; + float: right; + } +} + +#nav { + margin: 0 20px; + padding: 2px 10px; + 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); + + li { + margin: 0; + padding: 0; + list-style: none; + display: inline; + a { + display: block; + width: auto; + float: left; + height: 20px; + line-height: 20px; + text-align: center; + padding: 0 10px 0 32px; + margin-left: 10px; + color: #333; + border-radius: 20px; + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + background-position: 10px 50%; + background-repeat: no-repeat; + } + + a.active, a:hover { + background-color: #dbefff; + color: #000; + } + + a:hover { + background-color: #ededed; + color: #0054a6; + } + + a.home { background-image: url('../images/icons/home.png'); } + a.kb { background-image: url('../images/icons/kb.png'); } + a.new { background-image: url('../images/icons/new.png'); } + a.status { background-image: url('../images/icons/status.png'); } + a.tickets { background-image: url('../images/icons/tix.png'); } + } +} + +#content { + padding: 20px 0; + margin: 0 20px; + height: auto !important; + height: 350px; + min-height: 350px; +} + +#footer { + text-align: center; + font-size: 11px; + color: #333; + + a { + color: #333; + } + + p { + margin: 10px 0 0 0; + } + + #poweredBy { + display: block; + width: 126px; + height: 23px; + outline: none; + text-indent: -9999px; + margin: 0 auto; + background: url('../images/poweredby.png') top left no-repeat; + } +} \ No newline at end of file diff --git a/assets/default/less/print.less b/assets/default/less/print.less new file mode 100644 index 0000000000000000000000000000000000000000..dbe21bc7e8d2d2f16cbc7f6db5974383903357f7 --- /dev/null +++ b/assets/default/less/print.less @@ -0,0 +1,30 @@ +#header, #nav, #meta, #footer, #reply, #pagination, .reload, .refresh, form, .thread, hr, #kbAttachments, .back { + display: none; +} + +th { + text-align: left; +} + +a { + color: #000; + text-decoration: none; +} + +caption { + text-align: left; + padding-bottom: 10px; + font-weight: bold; +} + +.message, .response { + border-bottom: 1px solid #000; + margin-bottom: 20px; + padding-bottom: 10px; + + th { + font-size: 12pt; + font-weight: bold; + padding-bottom: 5px; + } +} diff --git a/assets/default/less/reset.less b/assets/default/less/reset.less new file mode 100644 index 0000000000000000000000000000000000000000..8cf9581eb2516d2c21e5b7b2be0bf614e85d9592 --- /dev/null +++ b/assets/default/less/reset.less @@ -0,0 +1,117 @@ +html { + font-size: 100%; + overflow-y: scroll; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; + font-size: 13px; + line-height: 1.231; + padding: 0; +} + +body, input, select, textarea { + font-family: sans-serif; + color: #000; +} + +b, strong { + font-weight: bold; +} + +blockquote { + margin: 1em 40px; +} + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} + +small { + font-size: 85%; +} + +ul, ol { + margin: 1em 0; + padding: 0 0 0 30px; +} + +img { + border: 0; + vertical-align: middle; +} + +form { + margin: 0; +} + +fieldset { + border: 0; + margin: 0; + padding: 0; +} + +label { + cursor: pointer; +} + +input, select, textarea { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle; +} + +input { + line-height: normal; + *overflow: visible; +} + +table input { + *overflow: auto; +} + +input[type="button"], input[type="reset"], input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +input[type="checkbox"], input[type="radio"] { + box-sizing: border-box; +} + +input[type="search"] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box; +} + +textarea { + overflow: auto; + vertical-align: top; + resize: vertical; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +th, td { + vertical-align: top; +} + +th { text-align: left; font-weight: normal; } + +h1, h2, h3, h4, h5, h6, form, fieldset { + margin: 0; + padding: 0; +} \ No newline at end of file diff --git a/assets/default/less/theme.less b/assets/default/less/theme.less new file mode 100644 index 0000000000000000000000000000000000000000..c3835f9e608d91533abb782a560b5f4c868cd675 --- /dev/null +++ b/assets/default/less/theme.less @@ -0,0 +1,7 @@ +@import 'reset'; +@import 'base'; +@import 'main-layout'; +@import 'landing-page'; +@import 'kb'; +@import 'ticket-forms'; +@import 'ticket'; diff --git a/assets/default/less/ticket-forms.less b/assets/default/less/ticket-forms.less new file mode 100644 index 0000000000000000000000000000000000000000..96db30b28a692d7db08e3389e2b35317f60b9859 --- /dev/null +++ b/assets/default/less/ticket-forms.less @@ -0,0 +1,170 @@ +#ticketForm, #clientLogin { + div { + clear: both; + padding: 3px 0; + overflow: hidden; + + label { + display: block; + width: 140px; + float: left; + } + + label.required { + font-weight: bold; + text-align: left; + } + + input, textarea { + width: auto; + border: 1px solid #aaa; + background: #fff; + margin-right: 10px; + display: block; + float: left; + } + + input[type=file] { + border: 0; + } + + select { + display: block; + float: left; + } + div.captchaRow { + line-height: 31px; + + input { + position: relative; + top: 6px; + } + } + + } + + td, div { + textarea { + width: 600px; + } + + em { + color: #777; + } + + .captcha { + width: 88px; + height: 31px; + background: #000; + display: block; + float: left; + margin-right: 20px; + } + + label.inline { + width: auto; + padding: 0 10px; + } + } + + div.error { + input { + border: 1px solid #a00; + } + label { + color: #a00; + } + } +} + +#ticketTable { + th { + width: 160px; + font-weight: normal; + text-align: left; + } + th.required, td.required { + font-weight: bold; + text-align: left; + } +} + +#clientLogin { + width: 400px; + margin-top: 20px; + padding: 10px 100px 10px 10px; + border: 1px solid #ccc; + background: url('../images/lock.png?1319655200') 440px 50% no-repeat #f6f6f6; + + p { + clear: both; + text-align: center; + } + + strong { + font-size: 11px; + color: #d00; + display: block; + padding-left: 140px; + } + + #email { + width: 250px; + margin-right: 0; + } + + #ticketno { + width: 120px; + margin-right: 0; + } +} + +#reply { + margin-top: 20px; + padding: 10px 5px; + background: #f9f9f9; + border: 1px solid #ccc; + + h2 { + margin-bottom: 10px; + } + + table { + width: 800px; + + td { + vertical-align: top; + } + } + + textarea { + width: 628px !important; + } + + 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; + } +} + +.uploads { + display:inline-block; + padding-right:20px; + + label { + padding:3px; + padding-right:10px; + width: auto !important + } +} \ No newline at end of file diff --git a/assets/default/less/ticket.less b/assets/default/less/ticket.less new file mode 100644 index 0000000000000000000000000000000000000000..d7f2d307930432c2b41e3396f716d77fca5bc3e9 --- /dev/null +++ b/assets/default/less/ticket.less @@ -0,0 +1,145 @@ +/* Ticket icons */ +.Icon { + width: auto; + padding-left: 20px; + background-position: top left; + background-repeat: no-repeat; + color: #006699; + text-decoration: none; +} + +.Icon.Ticket { background-image: url('../images/icons/ticket.gif') } +.Icon.webTicket { background-image: url('../images/icons/ticket_source_web.gif'); } +.Icon.emailTicket { background-image: url('../images/icons/ticket_source_email.gif'); } +.Icon.phoneTicket { background-image: url('../images/icons/ticket_source_phone.gif'); } +.Icon.otherTicket { background-image: url('../images/icons/ticket_source_other.gif'); } +.Icon.attachment { background-image: url('../images/icons/attachment.gif'); } +.Icon.file { background-image: url('../images/icons/attachment.gif'); } +.Icon.refresh { background-image: url('../images/icons/refresh.gif'); } + +.Icon.thread { + font-weight: bold; + font-size: 1em; + background-image: url('../images/icons/thread.gif?1319556657'); +} + +.Icon:hover { + text-decoration: underline; +} + + +#ticketTable { + border: 1px solid #aaa; + border-left: none; + border-bottom: none; + + caption { + padding: 5px; + text-align: left; + color: #000; + background: #ddd; + border: 1px solid #aaa; + border-bottom: none; + font-weight: bold; + } + + th { + height: 24px; + line-height: 24px; + background: #e1f2ff; + border: 1px solid #aaa; + border-right: none; + border-top: none; + + a { + color: #000; + } + } + + td { + padding: 2px; + border: 1px solid #aaa; + border-right: none; + border-top: none; + } + + tr.alt td { + background: #f9f9f9; + } +} + +#ticketSearchForm { + display: inline-block; + float: left; + padding: 0 0 5px 0; +} + +a.refresh { + display: block; + width: auto; + float: right; + height: 20px; + line-height: 20px; + text-align: center; + padding: 0 10px 0 28px; + border: 1px solid #aaa; + margin-left: 10px; + color: #333; + background-position: 5px 50%; + background-repeat: no-repeat; + background-image: url('../images/icons/refresh.png'); +} + +.infoTable { + background: #F4FAFF; + th { + text-align: left; + } +} + +#ticketThread { + table { + margin-top: 10px; + border: 1px solid #aaa; + border-bottom: 2px solid #aaa; + + th { + text-align: left; + border-bottom: 1px solid #aaa; + font-size: 12px; + padding: 5px; + + span { + font-weight:normal; + color:#888; + padding-left:20px; + } + } + + td { + padding: 5px; + } + } + + .message th { background: #d8efff; } + .response th { background: #ddd; } + + .info { + padding: 2px; + background: #f9f9f9; + border-top: 1px solid #ddd; + height: 16px; + line-height: 16px; + + a { + display: inline-block; + margin: 5px 10px 5px 0; + padding-left: 24px; + height: 16px; + line-height: 16px; + background-position: 0 50%; + background-repeat: no-repeat; + } + } +} + diff --git a/include/.htaccess b/include/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..3a42882788717c9ed1d5c2fcc3277d21ec13152b --- /dev/null +++ b/include/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/include/class.banlist.php b/include/class.banlist.php index 27729b44a42f45cb0d187f1e8f2a8b731991784e..c620b105033fbc1275b6f0cb82ee69bb2c5306d3 100644 --- a/include/class.banlist.php +++ b/include/class.banlist.php @@ -27,7 +27,7 @@ class Banlist { } function isbanned($email) { - return EmailFilter::isBanned($email); + return TicketFilter::isBanned($email); } function includes($email) { @@ -50,7 +50,7 @@ class Banlist { 'name' => 'SYSTEM BAN LIST', 'isactive' => 1, 'match_all_rules' => false, - 'reject_email' => true, + 'reject_ticket' => true, 'rules' => array(), 'notes' => 'Internal list for email banning. Do not remove' ), $errors); diff --git a/include/class.canned.php b/include/class.canned.php index 52a487529c368bc158777359260d36b808dc1d90..c84950e7d6d19b062dbb3a53c0ccfe95eab96c48 100644 --- a/include/class.canned.php +++ b/include/class.canned.php @@ -35,8 +35,10 @@ class Canned { .' count(filter.id) as filters ' .' FROM '.CANNED_TABLE.' canned ' .' LEFT JOIN '.CANNED_ATTACHMENT_TABLE.' attach ON (attach.canned_id=canned.canned_id) ' - .' LEFT JOIN '.EMAIL_FILTER_TABLE.' filter ON (canned.canned_id = filter.canned_response_id) ' - .' WHERE canned.canned_id='.db_input($id); + .' LEFT JOIN '.FILTER_TABLE.' filter ON (canned.canned_id = filter.canned_response_id) ' + .' WHERE canned.canned_id='.db_input($id) + .' GROUP BY canned.canned_id'; + if(!($res=db_query($sql)) || !db_num_rows($res)) return false; @@ -100,7 +102,7 @@ class Canned { if (!$this->_filters) { $this->_filters = array(); $res = db_query( - 'SELECT name FROM '.EMAIL_FILTER_TABLE + 'SELECT name FROM '.FILTER_TABLE .' WHERE canned_response_id = '.db_input($this->getId()) .' ORDER BY name'); while ($row = db_fetch_row($res)) diff --git a/include/class.config.php b/include/class.config.php index cc9b497682097d0e622dbbd410b0a4f5b0fae601..e6271511239683703825dbdaedc4066e819c5d4b 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -18,9 +18,8 @@ require_once(INCLUDE_DIR.'class.email.php'); class Config { - var $id=0; - var $mysqltzoffset=0; - var $config=array(); + var $id = 0; + var $config = array(); var $defaultDept; //Default Department var $defaultSLA; //Default SLA @@ -33,18 +32,27 @@ class Config { } function load($id=0) { + if(!$id && !($id=$this->getId())) return false; - $sql='SELECT * FROM '.CONFIG_TABLE + $sql='SELECT *, (TIME_TO_SEC(TIMEDIFF(NOW(), UTC_TIMESTAMP()))/3600) as db_tz_offset ' + .' FROM '.CONFIG_TABLE .' WHERE id='.db_input($id); + if(!($res=db_query($sql)) || !db_num_rows($res)) return false; - $this->config=db_fetch_array($res); - $this->id=$this->config['id']; - $this->setMysqlTZ(db_timezone()); + $this->config = db_fetch_array($res); + $this->id = $this->config['id']; + + //Get the default time zone + // We can't JOIN timezone table above due to upgrade support. + if($this->config['default_timezone_id']) + $this->config['tz_offset'] = Timezone::getOffsetById($this->config['default_timezone_id']); + else + $this->config['tz_offset'] = 0; return true; } @@ -92,17 +100,9 @@ class Config { return null; } - - function setMysqlTZ($tz) { - //TODO: Combine the 2 replace regex - if($tz=='SYSTEM') - $this->mysqltzoffset=preg_replace('/([+-]\d{2})(\d{2})/','\1',date('O')); - else - $this->mysqltzoffset=preg_replace('/([+-]\d{2})(:)(\d{2})/','\1',$tz); - } - function getMysqlTZoffset() { - return $this->mysqltzoffset; + function getDBTZoffset() { + return $this->config['db_tz_offset']; } /* Date & Time Formats */ @@ -149,7 +149,7 @@ class Config { } function getTZOffset() { - return $this->config['timezone_offset']; + return $this->config['tz_offset']; } function getPageSize() { @@ -543,35 +543,32 @@ class Config { return $this->config['upload_dir']; } - function updateSettings($vars,&$errors) { + function updateSettings($vars, &$errors) { if(!$vars || $errors) return false; switch(strtolower($vars['t'])) { - case 'general': - return $this->updateGeneralSetting($vars,$errors); - break; - case 'dates': - return $this->updateDateTimeSetting($vars,$errors); + case 'system': + return $this->updateSystemSettings($vars, $errors); break; case 'tickets': - return $this->updateTicketsSetting($vars,$errors); + return $this->updateTicketsSettings($vars, $errors); break; case 'emails': - return $this->updateEmailsSetting($vars,$errors); + return $this->updateEmailsSettings($vars, $errors); break; case 'attachments': return $this->updateAttachmentsSetting($vars,$errors); break; - case 'autoresponders': - return $this->updateAutoresponderSetting($vars,$errors); + case 'autoresp': + return $this->updateAutoresponderSettings($vars, $errors); break; case 'alerts': - return $this->updateAlertsSetting($vars,$errors); + return $this->updateAlertsSettings($vars, $errors); break; case 'kb': - return $this->updateKBSetting($vars,$errors); + return $this->updateKBSettings($vars, $errors); break; default: $errors['err']='Unknown setting option. Get technical support.'; @@ -580,7 +577,7 @@ class Config { return false; } - function updateGeneralSetting($vars, &$errors) { + function updateSystemSettings($vars, &$errors) { $f=array(); $f['helpdesk_url']=array('type'=>'string', 'required'=>1, 'error'=>'Helpdesk URl required'); @@ -589,6 +586,13 @@ class Config { $f['default_template_id']=array('type'=>'int', 'required'=>1, 'error'=>'You must select template.'); $f['staff_session_timeout']=array('type'=>'int', 'required'=>1, 'error'=>'Enter idle time in minutes'); $f['client_session_timeout']=array('type'=>'int', 'required'=>1, 'error'=>'Enter idle time in minutes'); + //Date & Time Options + $f['time_format']=array('type'=>'string', 'required'=>1, 'error'=>'Time format required'); + $f['date_format']=array('type'=>'string', 'required'=>1, 'error'=>'Date format required'); + $f['datetime_format']=array('type'=>'string', 'required'=>1, 'error'=>'Datetime format required'); + $f['daydatetime_format']=array('type'=>'string', 'required'=>1, 'error'=>'Day, Datetime format required'); + $f['default_timezone_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default Timezone required'); + if(!Validator::process($f, $vars, $errors) || $errors) return false; @@ -610,26 +614,6 @@ class Config { .',client_max_logins='.db_input($vars['client_max_logins']) .',client_login_timeout='.db_input($vars['client_login_timeout']) .',client_session_timeout='.db_input($vars['client_session_timeout']) - .',clickable_urls='.db_input(isset($vars['clickable_urls'])?1:0) - .',enable_auto_cron='.db_input(isset($vars['enable_auto_cron'])?1:0) - .' WHERE id='.db_input($this->getId()); - - return (db_query($sql)); - } - - function updateDateTimeSetting($vars,&$errors) { - - $f=array(); - $f['time_format']=array('type'=>'string', 'required'=>1, 'error'=>'Time format required'); - $f['date_format']=array('type'=>'string', 'required'=>1, 'error'=>'Date format required'); - $f['datetime_format']=array('type'=>'string', 'required'=>1, 'error'=>'Datetime format required'); - $f['daydatetime_format']=array('type'=>'string', 'required'=>1, 'error'=>'Day, Datetime format required'); - $f['default_timezone_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default Timezone required'); - - if(!Validator::process($f,$vars,$errors) || $errors) - return false; - - $sql='UPDATE '.CONFIG_TABLE.' SET updated=NOW() ' .',time_format='.db_input($vars['time_format']) .',date_format='.db_input($vars['date_format']) .',datetime_format='.db_input($vars['datetime_format']) @@ -641,7 +625,7 @@ class Config { return (db_query($sql)); } - function updateTicketsSetting($vars,&$errors) { + function updateTicketsSettings($vars, &$errors) { $f=array(); @@ -658,7 +642,30 @@ class Config { $errors['enable_captcha']='PNG support required for Image Captcha'; } - if(!Validator::process($f,$vars,$errors) || $errors) + if($vars['allow_attachments']) { + + if(!ini_get('file_uploads')) + $errors['err']='The \'file_uploads\' directive is disabled in php.ini'; + + if(!is_numeric($vars['max_file_size'])) + $errors['max_file_size']='Maximum file size required'; + + if(!$vars['allowed_filetypes']) + $errors['allowed_filetypes']='Allowed file extentions required'; + + if(!($maxfileuploads=ini_get('max_file_uploads'))) + $maxfileuploads=DEFAULT_MAX_FILE_UPLOADS; + + if(!$vars['max_user_file_uploads'] || $vars['max_user_file_uploads']>$maxfileuploads) + $errors['max_user_file_uploads']='Invalid selection. Must be less than '.$maxfileuploads; + + if(!$vars['max_staff_file_uploads'] || $vars['max_staff_file_uploads']>$maxfileuploads) + $errors['max_staff_file_uploads']='Invalid selection. Must be less than '.$maxfileuploads; + } + + + + if(!Validator::process($f, $vars, $errors) || $errors) return false; $sql='UPDATE '.CONFIG_TABLE.' SET updated=NOW() ' @@ -676,14 +683,24 @@ class Config { .',show_answered_tickets='.db_input(isset($vars['show_answered_tickets'])?1:0) .',show_related_tickets='.db_input(isset($vars['show_related_tickets'])?1:0) .',show_notes_inline='.db_input(isset($vars['show_notes_inline'])?1:0) + .',clickable_urls='.db_input(isset($vars['clickable_urls'])?1:0) .',hide_staff_name='.db_input(isset($vars['hide_staff_name'])?1:0) + .',allow_attachments='.db_input(isset($vars['allow_attachments'])?1:0) + .',allowed_filetypes='.db_input(strtolower(preg_replace("/\n\r|\r\n|\n|\r/", '',trim($vars['allowed_filetypes'])))) + .',max_file_size='.db_input($vars['max_file_size']) + .',max_user_file_uploads='.db_input($vars['max_user_file_uploads']) + .',max_staff_file_uploads='.db_input($vars['max_staff_file_uploads']) + .',email_attachments='.db_input(isset($vars['email_attachments'])?1:0) + .',allow_email_attachments='.db_input(isset($vars['allow_email_attachments'])?1:0) + .',allow_online_attachments='.db_input(isset($vars['allow_online_attachments'])?1:0) + .',allow_online_attachments_onlogin='.db_input(isset($vars['allow_online_attachments_onlogin'])?1:0) .' WHERE id='.db_input($this->getId()); return (db_query($sql)); } - function updateEmailsSetting($vars,&$errors) { + function updateEmailsSettings($vars, &$errors) { $f=array(); $f['default_email_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default email required'); @@ -704,6 +721,7 @@ class Config { .',alert_email_id='.db_input($vars['alert_email_id']) .',default_smtp_id='.db_input($vars['default_smtp_id']) .',admin_email='.db_input($vars['admin_email']) + .',enable_auto_cron='.db_input(isset($vars['enable_auto_cron'])?1:0) .',enable_mail_polling='.db_input(isset($vars['enable_mail_polling'])?1:0) .',enable_email_piping='.db_input(isset($vars['enable_email_piping'])?1:0) .',strip_quoted_reply='.db_input(isset($vars['strip_quoted_reply'])?1:0) @@ -756,7 +774,7 @@ class Config { return (db_query($sql)); } - function updateAutoresponderSetting($vars,&$errors) { + function updateAutoresponderSettings($vars, &$errors) { if($errors) return false; @@ -771,7 +789,7 @@ class Config { } - function updateKBSetting($vars,&$errors) { + function updateKBSettings($vars, &$errors) { if($errors) return false; @@ -784,7 +802,7 @@ class Config { } - function updateAlertsSetting($vars,&$errors) { + function updateAlertsSettings($vars, &$errors) { if($vars['ticket_alert_active'] diff --git a/include/class.file.php b/include/class.file.php index e4012bb4b9f7665438cc28fbc3818162f48579a7..b44f7af4542343ccd55e93c2db199e06ea343278 100644 --- a/include/class.file.php +++ b/include/class.file.php @@ -27,7 +27,8 @@ class AttachmentFile { if(!$id && !($id=$this->getId())) return false; - $sql='SELECT f.*, count(DISTINCT c.canned_id) as canned, count(DISTINCT t.ticket_id) as tickets ' + $sql='SELECT id, type, size, name, hash, f.created, ' + .' count(DISTINCT c.canned_id) as canned, count(DISTINCT t.ticket_id) as tickets ' .' FROM '.FILE_TABLE.' f ' .' LEFT JOIN '.CANNED_ATTACHMENT_TABLE.' c ON(c.file_id=f.id) ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' t ON(t.file_id=f.id) ' @@ -90,12 +91,24 @@ class AttachmentFile { return $this->ht['hash']; } - function getBinary() { - return $this->ht['filedata']; + function open() { + return new AttachmentChunkedData($this->id); + } + + function sendData() { + $file = $this->open(); + while ($chunk = $file->read()) + echo $chunk; } function getData() { - return $this->getBinary(); + # XXX: This is horrible, and is subject to php's memory + # restrictions, etc. Don't use this function! + ob_start(); + $this->sendData(); + $data = &ob_get_contents(); + ob_end_clean(); + return $data; } function delete() { @@ -110,7 +123,7 @@ class AttachmentFile { header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream')); header('Content-Length: '.$this->getSize()); - echo $this->getData(); + $this->sendData(); exit(); } @@ -132,7 +145,7 @@ class AttachmentFile { header('Content-Transfer-Encoding: binary'); header('Content-Length: '.$this->getSize()); - echo $this->getBinary(); + $this->sendData(); exit(); } @@ -168,15 +181,9 @@ class AttachmentFile { if (!(db_query($sql) && ($id=db_insert_id()))) return false; - foreach (str_split($file['data'], 1024*100) as $chunk) { - $sql='UPDATE '.FILE_TABLE - .' SET filedata = CONCAT(filedata,'.db_input($chunk).')' - .' WHERE id='.db_input($id); - if(!db_query($sql)) { - db_query('DELETE FROM '.FILE_TABLE.' WHERE id='.db_input($id).' LIMIT 1'); - return false; - } - } + $data = new AttachmentChunkedData($id); + if (!$data->write($file['data'])) + return false; return $id; } @@ -213,44 +220,56 @@ class AttachmentFile { .'SELECT file_id FROM '.FAQ_ATTACHMENT_TABLE .') still_loved' .')'); + AttachmentChunkedData::deleteOrphans(); return db_affected_rows(); } } -class AttachmentList { - function AttachmentList($table, $key) { - $this->table = $table; - $this->key = $key; +/** + * Attachments stored in the database are cut into 256kB chunks and stored + * in the FILE_CHUNK_TABLE to overcome the max_allowed_packet limitation of + * LOB fields in the MySQL database + */ +define('CHUNK_SIZE', 500*1024); # Beware if you change this... +class AttachmentChunkedData { + function AttachmentChunkedData($file) { + $this->_file = $file; + $this->_pos = 0; } - function all() { - if (!isset($this->list)) { - $this->list = array(); - $res=db_query('SELECT file_id FROM '.$this->table - .' WHERE '.$this->key); - while(list($id) = db_fetch_row($res)) { - $this->list[] = new AttachmentFile($id); - } - } - return $this->list; + function length() { + list($length) = db_fetch_row(db_query( + 'SELECT SUM(LENGTH(filedata)) FROM '.FILE_CHUNK_TABLE + .' WHERE file_id='.db_input($this->_file))); + return $length; } - - function getCount() { - return count($this->all()); + + function read() { + # Read requested length of data from attachment chunks + list($buffer) = @db_fetch_row(db_query( + 'SELECT filedata FROM '.FILE_CHUNK_TABLE.' WHERE file_id=' + .db_input($this->_file).' AND chunk_id='.$this->_pos++)); + return $buffer; } - function add($fileId) { - db_query( - 'INSERT INTO '.$this->table - .' SET '.$this->key - .' file_id='.db_input($fileId)); + function write($what, $chunk_size=CHUNK_SIZE) { + $offset=0; + for (;;) { + $block = substr($what, $offset, $chunk_size); + if (!$block) break; + if (!db_query('REPLACE INTO '.FILE_CHUNK_TABLE + .' SET filedata=0x'.bin2hex($block).', file_id=' + .db_input($this->_file).', chunk_id='.db_input($this->_pos++))) + return false; + $offset += strlen($block); + } + return true; } - function remove($fileId) { + function deleteOrpans() { db_query( - 'DELETE FROM '.$this->table - .' WHERE '.$this->key - .' AND file_id='.db_input($fileId)); + 'DELETE FROM '.FILE_CHUNK_TABLE.' WHERE file_id NOT IN ' + .'( SELECT id FROM '.FILE_TABLE.') still_loved'); + return db_affected_rows(); } } -?> diff --git a/include/class.filter.php b/include/class.filter.php index b6c81b42b60d0d4b8c67d3f7b33b1138695fa1e7..ce7d7d92cb273705bc458e32e6c7dd93cf8668e7 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -2,7 +2,7 @@ /********************************************************************* class.filter.php - Email Filter Class + Ticket Filter Peter Rotich <peter@osticket.com> Copyright (c) 2006-2012 osTicket @@ -18,7 +18,7 @@ class Filter { var $id; var $ht; - function Filter($id){ + function Filter($id) { $this->id=0; $this->load($id); } @@ -29,8 +29,8 @@ class Filter { return false; $sql='SELECT filter.*,count(rule.id) as rule_count ' - .' FROM '.EMAIL_FILTER_TABLE.' filter ' - .' LEFT JOIN '.EMAIL_FILTER_RULE_TABLE.' rule ON(rule.filter_id=filter.id) ' + .' FROM '.FILTER_TABLE.' filter ' + .' LEFT JOIN '.FILTER_RULE_TABLE.' rule ON(rule.filter_id=filter.id) ' .' WHERE filter.id='.db_input($id) .' GROUP BY filter.id'; @@ -47,27 +47,31 @@ class Filter { return $this->load($this->getId()); } - function getId(){ + function getId() { return $this->id; } - function getName(){ + function getTarget() { + return $this->ht['target']; + } + + function getName() { return $this->ht['name']; } - function getNotes(){ + function getNotes() { return $this->ht['notes']; } - function getInfo(){ + function getInfo() { return $this->ht; } - function getNumRules(){ + function getNumRules() { return $this->ht['rule_count']; } - function getExecOrder(){ + function getExecOrder() { return $this->ht['execorder']; } @@ -75,7 +79,7 @@ class Filter { return $this->ht['email_id']; } - function isActive(){ + function isActive() { return ($this->ht['isactive']); } @@ -83,23 +87,23 @@ class Filter { return !strcasecmp($this->getName(),'SYSTEM BAN LIST'); } - function getDeptId(){ + function getDeptId() { return $this->ht['dept_id']; } - function getPriorityId(){ + function getPriorityId() { return $this->ht['priority_id']; } - function getSLAId(){ + function getSLAId() { return $this->ht['sla_id']; } - function getStaffId(){ + function getStaffId() { return $this->ht['staff_id']; } - function getTeamId(){ + function getTeamId() { return $this->ht['team_id']; } @@ -107,36 +111,36 @@ class Filter { return $this->ht['canned_response_id']; } - function stopOnMatch(){ + function stopOnMatch() { return ($this->ht['stop_on_match']); } - function matchAllRules(){ + function matchAllRules() { return ($this->ht['match_all_rules']); } - function rejectEmail(){ - return ($this->ht['reject_email']); + function rejectOnMatch() { + return ($this->ht['reject_ticket']); } - function useReplyToEmail(){ + function useReplyToEmail() { return ($this->ht['use_replyto_email']); } - function disableAlerts(){ + function disableAlerts() { return ($this->ht['disable_autoresponder']); } - function sendAlerts(){ + function sendAlerts() { return (!$this->disableAlerts()); } - function getRules(){ + function getRules() { if (!$this->ht['rules']) { $rules=array(); //We're getting the rules...live because it gets cleared on update. - $sql='SELECT * FROM '.EMAIL_FILTER_RULE_TABLE.' WHERE filter_id='.db_input($this->getId()); - if(($res=db_query($sql)) && db_num_rows($res)){ + $sql='SELECT * FROM '.FILTER_RULE_TABLE.' WHERE filter_id='.db_input($this->getId()); + if(($res=db_query($sql)) && db_num_rows($res)) { while($row=db_fetch_array($res)) $rules[]=array('w'=>$row['what'],'h'=>$row['how'],'v'=>$row['val']); } @@ -145,11 +149,11 @@ class Filter { return $this->ht['rules']; } - function getFlatRules(){ //Format used on html... I'm ashamed + function getFlatRules() { //Format used on html... I'm ashamed $info=array(); - if(($rules=$this->getRules())){ - foreach($rules as $k=>$rule){ + if(($rules=$this->getRules())) { + foreach($rules as $k=>$rule) { $i=$k+1; $info["rule_w$i"]=$rule['w']; $info["rule_h$i"]=$rule['h']; @@ -169,7 +173,7 @@ class Filter { function removeRule($what, $how, $val) { - $sql='DELETE FROM '.EMAIL_FILTER_RULE_TABLE + $sql='DELETE FROM '.FILTER_RULE_TABLE .' WHERE filter_id='.db_input($this->getId()) .' AND what='.db_input($what) .' AND how='.db_input($how) @@ -187,17 +191,19 @@ class Filter { } function containsRule($what, $how, $val) { + $val = trim($val); if (isset($this->ht['rules'])) { + $match = array("w"=>$what, "h"=>$how, "v"=>$val); foreach ($this->ht['rules'] as $rule) { - if (array("w"=>$what, "h"=>$how, "v"=>$val) == $rule) { - return True; - } + if ($match == $rule) + return true; } - return False; + return false; + } else { # Fetch from database return 0 != db_count( - "SELECT COUNT(*) FROM ".EMAIL_FILTER_RULE_TABLE + "SELECT COUNT(*) FROM ".FILTER_RULE_TABLE ." WHERE filter_id=".db_input($this->id) ." AND what=".db_input($what)." AND how=".db_input($how) ." AND val=".db_input($val) @@ -208,35 +214,41 @@ class Filter { * Simple true/false if the rules defined for this filter match the * incoming email * - * $email is an ARRAY, which has valid keys - * *from - email address of sender - * name - name of sender - * subject - subject line of the email - * body - body content of the email (no attachments, please) + * $info is an ARRAY, which has valid keys + * email - FROM email address of the ticket owner + * name - name of ticket owner + * subject - subject line of the ticket + * body - body content of the message (no attachments, please) * reply-to - reply-to email address * reply-to-name - name of sender to reply-to * headers - array of email headers - * emailid - osTicket email id of recipient + * emailId - osTicket system email id */ - function matches($email) { + function matches($info) { + + if(!$info || !is_array($info)) return false; + $what = array( - "email" => $email['email'], - "subject" => $email['subject'], + 'email' => $info['email'], + 'subject' => $info['subject'], # XXX: Support reply-to too ? - "name" => $email['name'], - "body" => $email['message'] + 'name' => $info['name'], + 'body' => $info['message'] # XXX: Support headers ); $how = array( # how => array(function, null or === this, null or !== this) - "equal" => array("strcmp", 0), - "not_equal" => array("strcmp", null, 0), - "contains" => array("strpos", null, false), - "dn_contain"=> array("strpos", false) + 'equal' => array('strcmp', 0), + 'not_equal' => array('strcmp', null, 0), + 'contains' => array('strpos', null, false), + 'dn_contain'=> array('strpos', false) ); + $match = false; # Respect configured filter email-id - if ($this->getEmailId() && $this->getEmailId() != $email['emailId']) + if ($this->getEmailId() + && !strcasecmp($this->getTarget(), 'Email') + && $this->getEmailId() != $info['emailId']) return false; foreach ($this->getRules() as $rule) { @@ -265,7 +277,7 @@ class Filter { * If the matches() method returns TRUE, send the initial ticket to this * method to apply the filter actions defined */ - function apply(&$ticket, $email=null) { + function apply(&$ticket, $info=null) { # TODO: Disable alerting # XXX: Does this imply turning it on as well? (via ->sendAlerts()) if ($this->disableAlerts()) $ticket['autorespond']=false; @@ -279,22 +291,24 @@ class Filter { # XXX: Unset the other (of staffId or teamId) (?) if ($this->getStaffId()) $ticket['staffId']=$this->getStaffId(); elseif ($this->getTeamId()) $ticket['teamId']=$this->getTeamId(); - # Override name with reply-to information from the EmailFilter + # Override name with reply-to information from the TicketFilter # match - if ($this->useReplyToEmail() && $email['reply-to']) { - $ticket['email'] = $email['reply-to']; - if ($email['reply-to-name']) - $ticket['name'] = $email['reply-to-name']; + if ($this->useReplyToEmail() && $info['reply-to']) { + $ticket['email'] = $info['reply-to']; + if ($info['reply-to-name']) + $ticket['name'] = $info['reply-to-name']; } + + # Use canned response. if ($this->getCannedResponse()) $ticket['cannedResponseId'] = $this->getCannedResponse(); } /* static */ function getSupportedMatches() { return array( - 'name'=> "Sender's Name", - 'email'=> "Sender's Email", - 'subject'=> 'Email Subject', - 'body'=> 'Email Body/Text' + 'name'=> 'Name', + 'email'=> 'Email', + 'subject'=> 'Subject', + 'body'=> 'Body/Text' ); } /* static */ function getSupportedMatchTypes() { @@ -306,7 +320,7 @@ class Filter { ); } - function update($vars,&$errors){ + function update($vars,&$errors) { if(!Filter::save($this->getId(),$vars,$errors)) return false; @@ -316,47 +330,55 @@ class Filter { return true; } - function delete(){ + function delete() { $id=$this->getId(); - $sql='DELETE FROM '.EMAIL_FILTER_TABLE.' WHERE id='.db_input($id).' LIMIT 1'; - if(db_query($sql) && ($num=db_affected_rows())){ - db_query('DELETE FROM '.EMAIL_FILTER_RULE_TABLE.' WHERE filter_id='.db_input($id)); + $sql='DELETE FROM '.FILTER_TABLE.' WHERE id='.db_input($id).' LIMIT 1'; + if(db_query($sql) && ($num=db_affected_rows())) { + db_query('DELETE FROM '.FILTER_RULE_TABLE.' WHERE filter_id='.db_input($id)); } return $num; } /** static functions **/ - function create($vars,&$errors){ + function getTargets() { + return array( + 'Any' => 'Any', + 'Web' => 'Web Forms', + 'API' => 'API Calls', + 'Email' => 'Emails'); + } + + function create($vars,&$errors) { return Filter::save(0,$vars,$errors); } - function getIdByName($name){ + function getIdByName($name) { - $sql='SELECT id FROM '.EMAIL_FILTER_TABLE.' WHERE name='.db_input($name); + $sql='SELECT id FROM '.FILTER_TABLE.' WHERE name='.db_input($name); if(($res=db_query($sql)) && db_num_rows($res)) list($id)=db_fetch_row($res); return $id; } - function lookup($id){ + function lookup($id) { return ($id && is_numeric($id) && ($f= new Filter($id)) && $f->getId()==$id)?$f:null; } - function validate_rules($vars,&$errors){ + function validate_rules($vars,&$errors) { return self::save_rules(0,$vars,$errors); } - function save_rules($id,$vars,&$errors){ + function save_rules($id,$vars,&$errors) { $matches=array('name','email','subject','body','header'); $types=array('equal','not_equal','contains','dn_contain'); $rules=array(); for($i=1; $i<=25; $i++) { //Expecting no more than 25 rules... - if($vars["rule_w$i"] || $vars["rule_h$i"]){ + if($vars["rule_w$i"] || $vars["rule_h$i"]) { if(!$vars["rule_w$i"] || !in_array($vars["rule_w$i"],$matches)) $errors["rule_$i"]='Invalid match selection'; elseif(!$vars["rule_h$i"] || !in_array($vars["rule_h$i"],$types)) @@ -367,7 +389,7 @@ class Filter { $errors["rule_$i"]='Valid email required for the match type'; else //for everything-else...we assume it's valid. $rules[]=array('w'=>$vars["rule_w$i"],'h'=>$vars["rule_h$i"],'v'=>$vars["rule_v$i"]); - }elseif($vars["rule_v$i"]){ + }elseif($vars["rule_v$i"]) { $errors["rule_$i"]='Incomplete selection'; } } @@ -383,7 +405,7 @@ class Filter { if(!$id) return true; //When ID is 0 then assume it was just validation... //Clear existing rules...we're doing mass replace on each save!! - db_query('DELETE FROM '.EMAIL_FILTER_RULE_TABLE.' WHERE filter_id='.db_input($id)); + db_query('DELETE FROM '.FILTER_RULE_TABLE.' WHERE filter_id='.db_input($id)); $num=0; foreach($rules as $rule) { $rule['filter_id']=$id; @@ -394,39 +416,52 @@ class Filter { return $num; } - function save($id,$vars,&$errors){ + function save($id,$vars,&$errors) { if(!$vars['execorder']) - $errors['execorder']='Order required'; + $errors['execorder'] = 'Order required'; elseif(!is_numeric($vars['execorder'])) - $errors['execorder']='Must be numeric value'; + $errors['execorder'] = 'Must be numeric value'; if(!$vars['name']) - $errors['name']='Name required'; + $errors['name'] = 'Name required'; elseif(($sid=self::getIdByName($vars['name'])) && $sid!=$id) - $errors['name']='Name already in-use'; + $errors['name'] = 'Name already in-use'; if(!$errors && !self::validate_rules($vars,$errors) && !$errors['rules']) - $errors['rules']='Unable to validate rules as entered'; + $errors['rules'] = 'Unable to validate rules as entered'; + + $targets = self::getTargets(); + if(!$vars['target']) + $errors['target'] = 'Target required'; + else if(!is_numeric($vars['target']) && !$targets[$vars['target']]) + $errors['target'] = 'Unknown or invalid target'; if($errors) return false; - $sql=' updated=NOW() '. - ',isactive='.db_input($vars['isactive']). - ',name='.db_input($vars['name']). - ',execorder='.db_input($vars['execorder']). - ',email_id='.db_input($vars['email_id']). - ',dept_id='.db_input($vars['dept_id']). - ',priority_id='.db_input($vars['priority_id']). - ',sla_id='.db_input($vars['sla_id']). - ',match_all_rules='.db_input($vars['match_all_rules']). - ',stop_onmatch='.db_input(isset($vars['stop_onmatch'])?1:0). - ',reject_email='.db_input(isset($vars['reject_email'])?1:0). - ',use_replyto_email='.db_input(isset($vars['use_replyto_email'])?1:0). - ',disable_autoresponder='.db_input(isset($vars['disable_autoresponder'])?1:0). - ',canned_response_id='.db_input($vars['canned_response_id']). - ',notes='.db_input($vars['notes']); + $emailId = 0; + if(is_numeric($vars['target'])) { + $emailId = $vars['target']; + $vars['target'] = 'Email'; + } + + $sql=' updated=NOW() ' + .',isactive='.db_input($vars['isactive']) + .',target='.db_input($vars['target']) + .',name='.db_input($vars['name']) + .',execorder='.db_input($vars['execorder']) + .',email_id='.db_input($emailId) + .',dept_id='.db_input($vars['dept_id']) + .',priority_id='.db_input($vars['priority_id']) + .',sla_id='.db_input($vars['sla_id']) + .',match_all_rules='.db_input($vars['match_all_rules']) + .',stop_onmatch='.db_input(isset($vars['stop_onmatch'])?1:0) + .',reject_ticket='.db_input(isset($vars['reject_ticket'])?1:0) + .',use_replyto_email='.db_input(isset($vars['use_replyto_email'])?1:0) + .',disable_autoresponder='.db_input(isset($vars['disable_autoresponder'])?1:0) + .',canned_response_id='.db_input($vars['canned_response_id']) + .',notes='.db_input($vars['notes']); //Auto assign ID is overloaded... @@ -438,11 +473,11 @@ class Filter { $sql.=',staff_id=0,team_id=0 '; //no auto-assignment! if($id) { - $sql='UPDATE '.EMAIL_FILTER_TABLE.' SET '.$sql.' WHERE id='.db_input($id); + $sql='UPDATE '.FILTER_TABLE.' SET '.$sql.' WHERE id='.db_input($id); if(!db_query($sql)) $errors['err']='Unable to update the filter. Internal error occurred'; }else{ - $sql='INSERT INTO '.EMAIL_FILTER_TABLE.' SET '.$sql.',created=NOW() '; + $sql='INSERT INTO '.FILTER_TABLE.' SET '.$sql.',created=NOW() '; if(!db_query($sql) || !($id=db_insert_id())) $errors['err']='Unable to add filter. Internal error'; } @@ -464,14 +499,14 @@ class FilterRule { var $filter; - function FilterRule($id,$filterId=0){ + function FilterRule($id,$filterId=0) { $this->id=0; $this->load($id,$filterId); } function load($id,$filterId=0) { - $sql='SELECT rule.* FROM '.EMAIL_FILTER_RULE_TABLE.' rule ' + $sql='SELECT rule.* FROM '.FILTER_RULE_TABLE.' rule ' .' WHERE rule.id='.db_input($id); if($filterId) $sql.=' AND rule.filter_id='.db_input($filterId); @@ -529,9 +564,9 @@ class FilterRule { return true; } - function delete(){ + function delete() { - $sql='DELETE FROM '.EMAIL_FILTER_RULE_TABLE.' WHERE id='.db_input($this->getId()).' AND filter_id='.db_input($this->getFilterId()); + $sql='DELETE FROM '.FILTER_RULE_TABLE.' WHERE id='.db_input($this->getId()).' AND filter_id='.db_input($this->getFilterId()); return (db_query($sql) && db_affected_rows()); } @@ -559,12 +594,12 @@ class FilterRule { $sql.=',notes='.db_input($vars['notes']); if($id) { - $sql='UPDATE '.EMAIL_FILTER_RULE_TABLE.' SET '.$sql.' WHERE id='.db_input($id).' AND filter_id='.db_input($vars['filter_id']); + $sql='UPDATE '.FILTER_RULE_TABLE.' SET '.$sql.' WHERE id='.db_input($id).' AND filter_id='.db_input($vars['filter_id']); if(db_query($sql)) return true; } else { - $sql='INSERT INTO '.EMAIL_FILTER_RULE_TABLE.' SET created=NOW(), filter_id='.db_input($vars['filter_id']).', '.$sql; + $sql='INSERT INTO '.FILTER_RULE_TABLE.' SET created=NOW(), filter_id='.db_input($vars['filter_id']).', '.$sql; if(db_query($sql) && ($id=db_insert_id())) return $id; } @@ -579,50 +614,65 @@ class FilterRule { } /** - * Applies rules defined in the staff control panel "Email Filters". Each + * Applies rules defined in the admin control panel > Settings tab > "Ticket Filters". Each * filter can have up to 25 rules (*currently). This will attempt to match - * the incoming email against the defined rules, and, if the email matches, - * the ticket will be modified as described in the filter + * the incoming tickets against the defined rules, and, if the email matches, + * the ticket will be modified as described in the filter actions. */ -class EmailFilter { +class TicketFilter { + + var $target; + var $vars; + /** - * Construct a list of filters to handle a new ticket generated from an - * email or something with information common to email (such as API - * calls, etc). + * Construct a list of filters to handle a new ticket + * taking into account the source/origin of the ticket. * - * $email is an ARRAY, which has valid keys - * *email - email address of sender - * name - name of sender - * subject - subject line of the email - * email-id - id of osTicket email recipient address + * $vars is an ARRAY, which has valid keys + * *email - email address of user + * name - name of user + * subject - subject of the ticket + * emailId - id of osTicket's system email (for emailed tickets) * --------------- * @see Filter::matches() for a complete list of supported keys * - * $slow - if TRUE, every (active) filter will be fetched from the - * database and matched against the email. Otherwise, a subset - * of filters from the database that appear to have rules that - * deal with the data in the email will be considered. @see - * ::quickList() for more information. + * IF $vars is not provided, every (active) filter will be fetched from the + * database and matched against the incoming ticket. Otherwise, a subset + * of filters from the database that appear to have rules that + * deal with the data in the incoming ticket (based on $vars) will be considered. + * @see ::quickList() for more information. */ - function EmailFilter($email, $slow=false) { - $this->email = $email; - if ($slow) { - $this->build($this->getAllActive()); - } else { - $this->build( - $this->quickList($email['email'], $email['name'], - $email['subject'], $email['emailId'])); - } + function TicketFilter($origin, $vars=null) { + + $this->target = self::origin2target($origin); + $this->vars = ($vars && is_array($vars))?array_filter(array_map('trim', $vars)):null; + + //Init filters. + $this->build(); } - - function build($res) { + + function build() { + + //Clear any memoized filters $this->filters = array(); - while (list($id) = db_fetch_row($res)) - array_push($this->filters, new Filter($id)); + $this->short_list = array(); + + //Query DB for "possibly" matching filters. + $res = $this->vars?$this->quickList():$this->getAllActive(); + if($res) { + while (list($id) = db_fetch_row($res)) + array_push($this->filters, new Filter($id)); + } + return $this->filters; } + + function getTarget() { + return $this->target; + } + /** - * Fetches the short list of filters that match the email received in the + * Fetches the short list of filters that match the ticket vars received in the * constructor. This function is memoized so subsequent calls will * return immediately. */ @@ -630,17 +680,18 @@ class EmailFilter { if (!isset($this->short_list)) { $this->short_list = array(); foreach ($this->filters as $filter) - if ($filter->matches($this->email)) + if ($filter->matches($this->vars)) $this->short_list[] = $filter; } + return $this->short_list; } /** - * Determine if the filters that match the received email indicate that + * Determine if the filters that match the received vars indicate that * the email should be rejected * * Returns FALSE if the email should be acceptable. If the email should - * be rejected, the first filter that matches and has rejectEmail set is + * be rejected, the first filter that matches and has reject ticket set is * returned. */ function shouldReject() { @@ -649,7 +700,7 @@ class EmailFilter { # be blocked; however, don't unset $reject, because if it # was set by another rule that did not set stopOnMatch(), we # should still honor its configuration - if ($filter->rejectEmail()) return $filter; + if ($filter->rejectOnMatch()) return $filter; } return false; } @@ -659,17 +710,21 @@ class EmailFilter { */ function apply(&$ticket) { foreach ($this->getMatchingFilterList() as $filter) { - $filter->apply($ticket, $this->email); + $filter->apply($ticket, $this->vars); if ($filter->stopOnMatch()) break; } } /* static */ function getAllActive() { - $sql="SELECT id FROM ".EMAIL_FILTER_TABLE." WHERE isactive" - ." ORDER BY execorder"; + + $sql='SELECT id FROM '.FILTER_TABLE + .' WHERE isactive=1 ' + .' AND target IN ("Any", '.db_input($this->getTarget()).') ' + .' ORDER BY execorder'; return db_query($sql); } + /** * Fast lookup function to all filters that have at least one rule that * matches the received address or name or is not defined to match based @@ -693,45 +748,55 @@ class EmailFilter { * information from the database. Whether the filter will completely * match or not is determined in the Filter::matches() method. */ - /* static */ function quickList($addr, $name=false, $subj=false, - $emailid=0) { - $sql="SELECT DISTINCT filter_id FROM ".EMAIL_FILTER_RULE_TABLE." rule" - ." INNER JOIN ".EMAIL_FILTER_TABLE." filter" - ." ON (filter.id=rule.filter_id)" - ." WHERE filter.isactive"; - # Filter by recipient email-id if specified - if ($emailid) #TODO: Fix the logic here... - $sql.=" AND filter.email_id=".db_input($emailid); + function quickList() { + + if(!$this->vars || !$this->vars['email']) + return $this->getAllActive(); + + $sql='SELECT DISTINCT filter_id FROM '.FILTER_RULE_TABLE.' rule ' + .' INNER JOIN '.FILTER_TABLE.' filter ' + .' ON (filter.id=rule.filter_id) ' + .' WHERE filter.isactive ' + ." AND filter.target IN ('Any', ".db_input($this->getTarget()).') '; + + # Filter by system's email-id if specified + if($this->vars['emailId']) + $sql.=' AND (filter.email_id=0 OR filter.email_id='.db_input($this->vars['emailId']).')'; + # Include rules for sender-email, sender-name and subject as # requested - $sql.=" AND ((what='email' AND LOCATE(val,".db_input($addr)."))"; - if ($name) - $sql.=" OR (what='name' AND LOCATE(val,".db_input($name)."))"; - if ($subj) - $sql.=" OR (what='subject' AND LOCATE(val,".db_input($subj)."))"; + $sql.=" AND ((what='email' AND LOCATE(val, ".db_input($this->vars['email']).'))'; + if($this->vars['name']) + $sql.=" OR (what='name' AND LOCATE(val, ".db_input($this->vars['name']).'))'; + if($this->vars['subject']) + $sql.=" OR (what='subject' AND LOCATE(val, ".db_input($this->vars['subject']).'))'; # Also include filters that do not have any rules concerning either # sender-email-addresses or sender-names or subjects $sql.=") OR filter.id IN (" ." SELECT filter_id " - ." FROM ".EMAIL_FILTER_RULE_TABLE." rule" - ." INNER JOIN ".EMAIL_FILTER_TABLE." filter" + ." FROM ".FILTER_RULE_TABLE." rule" + ." INNER JOIN ".FILTER_TABLE." filter" ." ON (rule.filter_id=filter.id)" + ." WHERE filter.isactive" + ." AND filter.target IN('Any', ".db_input($this->getTarget()).")" ." GROUP BY filter_id" ." HAVING COUNT(*)-COUNT(NULLIF(what,'email'))=0"; - if ($name!==false) $sql.=" AND COUNT(*)-COUNT(NULLIF(what,'name'))=0"; - if ($subj!==false) $sql.=" AND COUNT(*)-COUNT(NULLIF(what,'subject'))=0"; + if (!$this->vars['name']) $sql.=" AND COUNT(*)-COUNT(NULLIF(what,'name'))=0"; + if (!$this->vars['subject']) $sql.=" AND COUNT(*)-COUNT(NULLIF(what,'subject'))=0"; # Also include filters that do not have match_all_rules set to and # have at least one rule 'what' type that wasn't considered $sql.=") OR filter.id IN (" ." SELECT filter_id" - ." FROM ".EMAIL_FILTER_RULE_TABLE." rule" - ." INNER JOIN ".EMAIL_FILTER_TABLE." filter" + ." FROM ".FILTER_RULE_TABLE." rule" + ." INNER JOIN ".FILTER_TABLE." filter" ." ON (rule.filter_id=filter.id)" - ." WHERE what NOT IN ('email'" + ." WHERE filter.isactive" + ." AND filter.target IN ('Any', ".db_input($this->getTarget()).")" + ." AND what NOT IN ('email'" # Handle sender-name and subject if specified - .(($name!==false)?",'name'":"") - .(($subj!==false)?",'subject'":"") - .") AND filter.match_all_rules = false" + .((!$this->vars['name'])?",'name'":"") + .((!$this->vars['subject'])?",'subject'":"") + .") AND filter.match_all_rules = 0 " # Return filters in declared execution order .") ORDER BY filter.execorder"; @@ -751,10 +816,10 @@ class EmailFilter { /* static */ function isBanned($addr) { $sql='SELECT filter.id, what, how, UPPER(val) ' - .' FROM '.EMAIL_FILTER_TABLE.' filter' - .' INNER JOIN '.EMAIL_FILTER_RULE_TABLE.' rule' + .' FROM '.FILTER_TABLE.' filter' + .' INNER JOIN '.FILTER_RULE_TABLE.' rule' .' ON (filter.id=rule.filter_id)' - .' WHERE filter.reject_email' + .' WHERE filter.reject_ticket' .' AND filter.match_all_rules=0' .' AND filter.email_id=0' .' AND filter.isactive' @@ -762,20 +827,22 @@ class EmailFilter { .' AND rule.what="email"' .' AND LOCATE(rule.val,'.db_input($addr).')'; + if(!($res=db_query($sql)) || !db_num_rows($res)) + return false; + # XXX: Use MB_xxx function for proper unicode support $addr = strtoupper($addr); $how=array('equal' => array('strcmp', 0), 'contains' => array('strpos', null, false)); - - if ($res=db_query($sql)) { - while ($row=db_fetch_array($res)) { - list($func, $pos, $neg) = $how[$row['how']]; - if (!$func) continue; - $res = call_user_func($func, $addr, $row['val']); - if (($neg === null && $res === $pos) || $res !== $neg) - return $row['id']; - } + + while ($row=db_fetch_array($res)) { + list($func, $pos, $neg) = $how[$row['how']]; + if (!$func) continue; + $result = call_user_func($func, $addr, $row['val']); + if (($neg === null && $result === $pos) || $result !== $neg) + return $row['id']; } + return false; } @@ -814,5 +881,15 @@ class EmailFilter { } return false; } + + /** + * Normalize ticket source to supported filter target + * + */ + function origin2target($origin) { + $sources=array('web' => 'Web', 'email' => 'Email', 'phone' => 'Web', 'staff' => 'Web', 'api' => 'API'); + + return $sources[strtolower($origin)]; + } } ?> diff --git a/include/class.format.php b/include/class.format.php index 9de1197edbb3d4272193f67af48826851f72296e..aaa6667d32c2fce14da2cb9f5e15387ec9608b0c 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -180,7 +180,7 @@ class Format { } /* elapsed time */ - function elapsedTime($sec){ + function elapsedTime($sec) { if(!$sec || !is_numeric($sec)) return ""; @@ -197,32 +197,32 @@ class Format { /* Dates helpers...most of this crap will change once we move to PHP 5*/ function db_date($time) { global $cfg; - return Format::userdate($cfg->getDateFormat(),Misc::db2gmtime($time)); + return Format::userdate($cfg->getDateFormat(), Misc::db2gmtime($time)); } function db_datetime($time) { global $cfg; - return Format::userdate($cfg->getDateTimeFormat(),Misc::db2gmtime($time)); + return Format::userdate($cfg->getDateTimeFormat(), Misc::db2gmtime($time)); } function db_daydatetime($time) { global $cfg; - return Format::userdate($cfg->getDayDateTimeFormat(),Misc::db2gmtime($time)); + return Format::userdate($cfg->getDayDateTimeFormat(), Misc::db2gmtime($time)); } - function userdate($format,$gmtime) { - return Format::date($format,$gmtime,$_SESSION['TZ_OFFSET'],$_SESSION['TZ_DST']); + function userdate($format, $gmtime) { + return Format::date($format, $gmtime, $_SESSION['TZ_OFFSET'], $_SESSION['TZ_DST']); } - function date($format,$gmtimestamp,$offset=0,$daylight=false){ - if(!$gmtimestamp || !is_numeric($gmtimestamp)) return ""; - - $offset+=$daylight?date('I',$gmtimestamp):0; //Daylight savings crap. - return date($format,($gmtimestamp+($offset*3600))); - } - + function date($format, $gmtimestamp, $offset=0, $daylight=false){ - - + if(!$gmtimestamp || !is_numeric($gmtimestamp)) + return ""; + + $offset+=$daylight?date('I', $gmtimestamp):0; //Daylight savings crap. + + return date($format, ($gmtimestamp+ ($offset*3600))); + } + } ?> diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index fb8402169e83b39c258118329215d2459d7547a2..5a4e7e0f73e2edbe006f485f9dbe50b6d69809ce 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -376,7 +376,7 @@ class MailFetcher { return true; //Reporting success so the email can be moved or deleted. //Is the email address banned? - if($mailinfo['email'] && EmailFilter::isBanned($mailinfo['email'])) { + if($mailinfo['email'] && TicketFilter::isBanned($mailinfo['email'])) { //We need to let admin know... $ost->logWarning('Ticket denied', 'Banned email - '.$mailinfo['email']); return true; //Report success (moved or delete) diff --git a/include/class.misc.php b/include/class.misc.php index 9fd6744ba8913061a5e34c2b10bf705216d286fa..8149a1fce37ea3a26106f6bb217d1c92cc17395e 100644 --- a/include/class.misc.php +++ b/include/class.misc.php @@ -35,7 +35,7 @@ class Misc { if(!$var) return; $dbtime=is_int($var)?$var:strtotime($var); - return $dbtime-($cfg->getMysqlTZoffset()*3600); + return $dbtime-($cfg->getDBTZoffset()*3600); } //Take user time or gmtime and return db (mysql) time. @@ -50,7 +50,7 @@ class Misc { $time=$time-($offset*3600); } //gm to db time - return $time+($cfg->getMysqlTZoffset()*3600); + return $time+($cfg->getDBTZoffset()*3600); } /*Helper get GM time based on timezone offset*/ diff --git a/include/class.nav.php b/include/class.nav.php index 0d5fddcbf3a48772db052a71363bfe0fb3b64ad5..eb66293770888faa572cce786b2d2b22c868ed67 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -43,7 +43,7 @@ class StaffNav { return (!$this->isAdminPanel()); } - function setTabActive($tab){ + function setTabActive($tab, $menu=''){ if($this->tabs[$tab]){ $this->tabs[$tab]['active']=true; @@ -51,6 +51,7 @@ class StaffNav { $this->tabs[$this->activetab]['active']=false; $this->activetab=$tab; + if($menu) $this->setActiveSubMenu($menu, $tab); return true; } @@ -58,16 +59,25 @@ class StaffNav { return false; } - function setActiveTab($tab){ - return $this->setTabActive($tab); + function setActiveTab($tab, $menu=''){ + return $this->setTabActive($tab, $menu); } function getActiveTab(){ return $this->activetab; } - function setActiveSubMenu($mid) { - $this->activeMenu = $mid; + function setActiveSubMenu($mid, $tab='') { + if(is_numeric($mid)) + $this->activeMenu = $mid; + elseif($mid && $tab && ($subNav=$this->getSubNav($tab))) { + foreach($subNav as $k => $menu) { + if(strcasecmp($mid, $menu['href'])) continue; + + $this->activeMenu = $k+1; + break; + } + } } function getActiveMenu() { @@ -162,12 +172,11 @@ class AdminNav extends StaffNav{ if(!$this->tabs){ $tabs=array(); - $tabs['dashboard']=array('desc'=>'Dashboard','href'=>'admin.php','title'=>'Admin Dashboard'); + $tabs['dashboard']=array('desc'=>'Dashboard','href'=>'logs.php','title'=>'Admin Dashboard'); $tabs['settings']=array('desc'=>'Settings','href'=>'settings.php','title'=>'System Settings'); + $tabs['manage']=array('desc'=>'Manage','href'=>'helptopics.php','title'=>'Manage Options'); $tabs['emails']=array('desc'=>'Emails','href'=>'emails.php','title'=>'Email Settings'); - $tabs['topics']=array('desc'=>'Help Topics','href'=>'helptopics.php','title'=>'Help Topics'); - $tabs['staff']=array('desc'=>'Staff','href'=>'staff.php','title'=>'Staff Members'); - $tabs['depts']=array('desc'=>'Departments','href'=>'departments.php','title'=>'Departments'); + $tabs['staff']=array('desc'=>'Staff','href'=>'staff.php','title'=>'Manage Staff'); $this->tabs=$tabs; } @@ -184,37 +193,32 @@ class AdminNav extends StaffNav{ $subnav[]=array('desc'=>'System Logs','href'=>'logs.php','iconclass'=>'logs'); break; case 'settings': - $subnav[]=array('desc'=>'Settings & Preferences','href'=>'settings.php','iconclass'=>'preferences'); + $subnav[]=array('desc'=>'System Preferences','href'=>'settings.php?t=system','iconclass'=>'preferences'); + $subnav[]=array('desc'=>'Tickets','href'=>'settings.php?t=tickets','iconclass'=>'ticket-settings'); + $subnav[]=array('desc'=>'Emails','href'=>'settings.php?t=emails','iconclass'=>'email-settings'); + $subnav[]=array('desc'=>'Knowledgebase','href'=>'settings.php?t=kb','iconclass'=>'kb-settings'); + $subnav[]=array('desc'=>'Autoresponder','href'=>'settings.php?t=autoresp','iconclass'=>'email-autoresponders'); + $subnav[]=array('desc'=>'Alerts & Notices','href'=>'settings.php?t=alerts','iconclass'=>'alert-settings'); + break; + case 'manage': + $subnav[]=array('desc'=>'Help Topics','href'=>'helptopics.php','iconclass'=>'helpTopics'); + $subnav[]=array('desc'=>'Ticket Filters','href'=>'filters.php', + 'title'=>'Ticket Filters','iconclass'=>'ticketFilters'); $subnav[]=array('desc'=>'SLA Plans','href'=>'slas.php','iconclass'=>'sla'); $subnav[]=array('desc'=>'API Keys','href'=>'apikeys.php','iconclass'=>'api'); break; case 'emails': - $subnav[]=array('desc'=>'Email Addresses','href'=>'emails.php','iconclass'=>'emailSettings'); - $subnav[]=array('desc'=>'Email Filters','href'=>'filters.php', - 'title'=>'Email Filters','iconclass'=>'emailFilters'); - $subnav[]=array('desc'=>'Email Banlist','href'=>'banlist.php', + $subnav[]=array('desc'=>'Emails','href'=>'emails.php', 'title'=>'Email Addresses', 'iconclass'=>'emailSettings'); + $subnav[]=array('desc'=>'Banlist','href'=>'banlist.php', 'title'=>'Banned Emails','iconclass'=>'emailDiagnostic'); - $subnav[]=array('desc'=>'Email Templates','href'=>'templates.php','title'=>'Email Templates','iconclass'=>'emailTemplates'); - $subnav[]=array('desc'=>'Email Diagnostic','href'=>'emailtest.php','iconclass'=>'emailDiagnostic'); - break; - case 'topics': - $subnav[]=array('desc'=>'Help Topics','href'=>'helptopics.php','iconclass'=>'helpTopics'); - $subnav[]=array('desc'=>'Add New Help Topics', - 'href'=>'helptopics.php?a=add', - 'iconclass'=>'newHelpTopic', - 'droponly'=>true); + $subnav[]=array('desc'=>'Templates','href'=>'templates.php','title'=>'Email Templates','iconclass'=>'emailTemplates'); + $subnav[]=array('desc'=>'Diagnostic','href'=>'emailtest.php', 'title'=>'Email Diagnostic', 'iconclass'=>'emailDiagnostic'); break; case 'staff': $subnav[]=array('desc'=>'Staff Members','href'=>'staff.php','iconclass'=>'users'); $subnav[]=array('desc'=>'Teams','href'=>'teams.php','iconclass'=>'teams'); $subnav[]=array('desc'=>'Groups','href'=>'groups.php','iconclass'=>'groups'); - break; - case 'depts': $subnav[]=array('desc'=>'Departments','href'=>'departments.php','iconclass'=>'departments'); - $subnav[]=array('desc'=>'Add New Department', - 'href'=>'departments.php?a=add', - 'iconclass'=>'newDepartment', - 'droponly'=>true); break; } if($subnav) diff --git a/include/class.pdf.php b/include/class.pdf.php index 2257dcc17aa6641fabeeac32a3fcecc28bd7b2c0..17c2e05886538028468952af9518e2695e51793f 100644 --- a/include/class.pdf.php +++ b/include/class.pdf.php @@ -82,12 +82,24 @@ class Ticket2PDF extends FPDF $this->Cell(0, 7, 'Page ' . ($this->PageNo() - $this->pageOffset), 0, 0, 'R'); } + function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') { + parent::Cell($w, $h, $this->_utf8($txt), $border, $ln, $align, $fill, $link); + } + function WriteText($w, $text, $border) { $this->SetFont('Times','',11); $this->MultiCell($w, 5, $text, $border, 'L'); } + + function _utf8($text) { + + if(function_exists('iconv')) + return iconv('UTF-8', 'windows-1252', $text); + + return utf8_encode($text); + } function _print() { @@ -95,7 +107,7 @@ class Ticket2PDF extends FPDF return; $w =(($this->w/2)-$this->lMargin); - $l = 40; + $l = 35; $c = $w-$l; $this->SetDrawColor(220, 220, 220); $this->SetFillColor(244, 250, 255); diff --git a/include/class.ticket.php b/include/class.ticket.php index d15b18c506ee9188d7b55c19eae9d6cb2b23680b..bfa23d491f127552e2895c33464eb6d8d9d72995 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -1331,7 +1331,7 @@ class Ticket{ if($newticket) return $msgid; //Our work is done... $autorespond = true; - if ($autorespond && $headers && EmailFilter::isAutoResponse(Mail_Parse::splitHeaders($headers))) + if ($autorespond && $headers && TicketFilter::isAutoResponse(Mail_Parse::splitHeaders($headers))) $autorespond=false; $this->onMessage($autorespond); //must be called b4 sending alerts to staff. @@ -1845,7 +1845,7 @@ class Ticket{ if ($vars['email'] && Validator::is_email($vars['email'])) { //Make sure the email address is not banned - if(EmailFilter::isBanned($vars['email'])) { + if(TicketFilter::isBanned($vars['email'])) { $errors['err']='Ticket denied. Error #403'; $ost->logWarning('Ticket denied', 'Banned email - '.$vars['email']); return 0; @@ -1865,12 +1865,15 @@ class Ticket{ return 0; } } + + //Init ticket filters... + $ticket_filter = new TicketFilter($origin, $vars); // Make sure email contents should not be rejected - if (($email_filter=new EmailFilter($vars)) - && ($filter=$email_filter->shouldReject())) { + if($ticket_filter + && ($filter=$ticket_filter->shouldReject())) { $errors['err']='Ticket denied. Error #403'; $ost->logWarning('Ticket denied', - sprintf('Banned email - %s by filter "%s"', + sprintf('Ticket rejected ( %s) by filter "%s"', $vars['email'], $filter->getName())); return 0; @@ -1915,7 +1918,7 @@ class Ticket{ } //Make sure the due date is valid - if($vars['duedate']){ + if($vars['duedate']) { if(!$vars['time'] || strpos($vars['time'],':')===false) $errors['time']='Select time'; elseif(strtotime($vars['duedate'].' '.$vars['time'])===false) @@ -1925,10 +1928,10 @@ class Ticket{ } //Any error above is fatal. - if ($errors) return 0; + if($errors) return 0; - # Perform email filter actions on the new ticket arguments XXX: Move filter to the top and check for reject... - if ($email_filter) $email_filter->apply($vars); + # Perform ticket filter actions on the new ticket arguments + if ($ticket_filter) $ticket_filter->apply($vars); # Some things will need to be unpacked back into the scope of this # function @@ -1955,11 +1958,7 @@ class Ticket{ if($autorespond) $autorespond=$email->autoRespond(); $email=null; $source='Email'; - }elseif($vars['deptId']){ //Opened by staff. - $deptId=$vars['deptId']; - $source=ucfirst($vars['source']); } - //Last minute checks $priorityId=$priorityId?$priorityId:$cfg->getDefaultPriorityId(); $deptId=$deptId?$deptId:$cfg->getDefaultDeptId(); @@ -2023,7 +2022,7 @@ class Ticket{ # Messages that are clearly auto-responses from email systems should # not have a return 'ping' message if ($autorespond && $vars['header'] && - EmailFilter::isAutoResponse(Mail_Parse::splitHeaders($vars['header']))) { + TicketFilter::isAutoResponse(Mail_Parse::splitHeaders($vars['header']))) { $autorespond=false; } diff --git a/include/class.upgrader.php b/include/class.upgrader.php index 7fa6c331ff542c44e7f4329c4e164f0848cf837e..2c898f646d7dd392790a55007774be1ae54c92a3 100644 --- a/include/class.upgrader.php +++ b/include/class.upgrader.php @@ -269,8 +269,6 @@ class Upgrader extends SetupWizard { $tasks=array(); switch($phash) { //Add patch specific scripted tasks. case 'c00511c7-7be60a84': //V1.6 ST- 1.7 * {{MD5('1.6 ST') -> c00511c7c1db65c0cfad04b4842afc57}} - $tasks[] = array('func' => 'migrateAttachments2DB', - 'desc' => 'Migrating attachments to database, it might take a while depending on the number of files.'); $tasks[] = array('func' => 'migrateSessionFile2DB', 'desc' => 'Transitioning to db-backed sessions'); break; @@ -282,6 +280,10 @@ class Upgrader extends SetupWizard { $tasks[] = array('func' => 'migrateGroupDeptAccess', 'desc' => 'Migrating group\'s department access to a new table'); break; + case '15b30765-dd0022fb': + $tasks[] = array('func' => 'migrateAttachments2DB', + 'desc' => 'Migrating attachments to database, it might take a while depending on the number of files.'); + break; } //Check IF SQL cleanup exists. diff --git a/include/client/header.inc.php b/include/client/header.inc.php index da763a7bbf7b1e5b9481b8d26a0dfca2c79a9997..81191f98b7dacefb7bc34ba03d6b53920d9d16a7 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -10,7 +10,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <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"> - <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/theme.css" media="screen"> + <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/theme.min.css" media="screen"> <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/print.css" media="print"> <script src="<?php echo ROOT_PATH; ?>js/jquery-1.7.2.min.js"></script> <script src="<?php echo ROOT_PATH; ?>js/jquery.multifile.js"></script> diff --git a/include/staff/banlist.inc.php b/include/staff/banlist.inc.php index 0b61d1e8b779edaf48c448c82eb38c3d1ce3deed..b2b37b284b0ae4de0750650b2feb9fae2135cf5d 100644 --- a/include/staff/banlist.inc.php +++ b/include/staff/banlist.inc.php @@ -3,7 +3,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$filter) $qstr=''; $select='SELECT rule.* '; -$from='FROM '.EMAIL_FILTER_RULE_TABLE.' rule '; +$from='FROM '.FILTER_RULE_TABLE.' rule '; $where='WHERE rule.filter_id='.db_input($filter->getId()); $search=false; if($_REQUEST['q'] && strlen($_REQUEST['q'])>3) { diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php index c2aad0bcf4456d5bade4fa323a4ba3c991c39929..36186838bd93cdba6977add500645becba4d673e 100644 --- a/include/staff/filter.inc.php +++ b/include/staff/filter.inc.php @@ -33,7 +33,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <tr> <th colspan="2"> <h4><?php echo $title; ?></h4> - <em>Filters are executed based on execution order.</em> + <em>Filters are executed based on execution order. Filter can target specific ticket source.</em> </th> </tr> </thead> @@ -71,31 +71,37 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> </tr> <tr> - <td width="180"> - To Email Address: + <td width="180" class="required"> + Target: </td> <td> - <select name="email_id"> - <option value="0">— Filter applies to ALL incoming emails ‐</option> - <?php + <select name="target"> + <option value="">— Select a Target ‐</option> + <?php + foreach(Filter::getTargets() as $k => $v) { + echo sprintf('<option value="%s" %s>%s</option>', + $k, (($k==$info['target'])?'selected="selected"':''), $v); + } $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' email ORDER by name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$email,$name)=db_fetch_row($res)){ + if(($res=db_query($sql)) && db_num_rows($res)) { + echo '<OPTGROUP label="Specific System Email">'; + while(list($id,$email,$name)=db_fetch_row($res)) { $selected=($info['email_id'] && $id==$info['email_id'])?'selected="selected"':''; if($name) $email=Format::htmlchars("$name <$email>"); echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$email); } + echo '</OPTGROUP>'; } ?> </select> - <br><em>(Highly recommended if the filter is specific to one incoming email address)</em> + + <span class="error">* <?php echo $errors['target']; ?></span> </td> </tr> <tr> <th colspan="2"> - <em><strong>Filter Rules</strong>: Rules are applied based on the criteria. - <span class="error">* <?php echo $errors['rules']; ?></span></em> + <em><strong>Filter Rules</strong>: Rules are applied based on the criteria. <span class="error">* <?php echo $errors['rules']; ?></span></em> </th> </tr> <tr> @@ -156,11 +162,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> <tr> <td width="180"> - Ban Email: + Reject Ticket: </td> <td> - <input type="checkbox" name="reject_email" value="1" <?php echo $info['reject_email']?'checked="checked"':''; ?> > - <strong><font class="error">Reject email</font></strong> <em>(All other actions, rules and filters are ignored)</em> + <input type="checkbox" name="reject_ticket" value="1" <?php echo $info['reject_ticket']?'checked="checked"':''; ?> > + <strong><font class="error">Reject Ticket</font></strong> <em>(All other actions and filters are ignored)</em> </td> </tr> <tr> diff --git a/include/staff/filters.inc.php b/include/staff/filters.inc.php index 7f3aab393f56cb3b1eb3f8dc3da0113204d35e41..7573eceec7d3a82950a52e9e04ce2fce213754e8 100644 --- a/include/staff/filters.inc.php +++ b/include/staff/filters.inc.php @@ -1,13 +1,13 @@ <?php if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); - +$targets = Filter::getTargets(); $qstr=''; $sql='SELECT filter.*,count(rule.id) as rules '. - 'FROM '.EMAIL_FILTER_TABLE.' filter '. - 'LEFT JOIN '.EMAIL_FILTER_RULE_TABLE.' rule ON(rule.filter_id=filter.id) '. + 'FROM '.FILTER_TABLE.' filter '. + 'LEFT JOIN '.FILTER_RULE_TABLE.' rule ON(rule.filter_id=filter.id) '. 'GROUP BY filter.id'; $sortOptions=array('name'=>'filter.name','status'=>'filter.isactive','order'=>'filter.execorder','rules'=>'rules', - 'created'=>'filter.created','updated'=>'filter.updated'); + 'target'=>'filter.target', 'created'=>'filter.created','updated'=>'filter.updated'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); $sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; //Sorting options... @@ -28,7 +28,7 @@ $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; $order_by="$order_column $order "; -$total=db_count('SELECT count(*) FROM '.EMAIL_FILTER_TABLE.' filter '); +$total=db_count('SELECT count(*) FROM '.FILTER_TABLE.' filter '); $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; $pageNav=new Pagenate($total, $page, PAGE_LIMIT); $pageNav->setURL('filters.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order'])); @@ -44,7 +44,7 @@ else ?> <div style="width:700;padding-top:5px; float:left;"> - <h2>Email Filters</h2> + <h2>Ticket Filters</h2> </div> <div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"> <b><a href="filters.php?a=add" class="Icon newEmailFilter">Add New Filter</a></b></div> @@ -58,9 +58,10 @@ else <tr> <th width="7"> </th> <th width="320"><a <?php echo $name_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=name">Name</a></th> - <th width="100"><a <?php echo $status_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=status">Status</a></th> + <th width="80"><a <?php echo $status_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=status">Status</a></th> <th width="80" style="text-align:center;"><a <?php echo $order_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=order">Order</a></th> <th width="80" style="text-align:center;"><a <?php echo $rules_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=rules">Rules</a></th> + <th width="100"><a <?php echo $target_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=target">Target</a></th> <th width="120" nowrap><a <?php echo $created_sort; ?>href="filters.php?<?php echo $qstr; ?>&sort=created">Date Added</a></th> <th width="150" nowrap><a <?php echo $updated_sort; ?>href="filters.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th> </tr> @@ -85,6 +86,7 @@ else <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td> <td style="text-align:right;padding-right:25px;"><?php echo $row['execorder']; ?> </td> <td style="text-align:right;padding-right:25px;"><?php echo $row['rules']; ?> </td> + <td> <?php echo Format::htmlchars($targets[$row['target']]); ?></td> <td> <?php echo Format::db_date($row['created']); ?></td> <td> <?php echo Format::db_datetime($row['updated']); ?></td> </tr> @@ -93,7 +95,7 @@ else endif; ?> <tfoot> <tr> - <td colspan="7"> + <td colspan="8"> <?php if($res && $num){ ?> Select: <a href="#" onclick="return select_all(document.forms['filters'],true)">All</a> diff --git a/include/staff/login.tpl.php b/include/staff/login.tpl.php index 2d8a41f650601c2ec8396ecb89a8ea5ac90a4790..2b4d21ad015f1e2d6124c8a3422572bcce53d91e 100644 --- a/include/staff/login.tpl.php +++ b/include/staff/login.tpl.php @@ -9,6 +9,12 @@ <meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> + <script type="text/javascript" src="../js/jquery-1.7.2.min.js"></script> + <script type="text/javascript"> + $(document).ready(function() { + $("input:not(.dp):visible:enabled:first").focus(); + }); + </script> </head> <body id="loginBody"> <div id="loginBox"> diff --git a/include/staff/settings-alerts.inc.php b/include/staff/settings-alerts.inc.php index 5bb5d393246ede7266440c30a7e9f5e41df8cc8e..cc91ffdf804fce3dad988a6e11139a1b259d60c8 100644 --- a/include/staff/settings-alerts.inc.php +++ b/include/staff/settings-alerts.inc.php @@ -1,102 +1,177 @@ +<h2>Alerts and Notices</h2> <form action="settings.php?t=alerts" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="t" value="alerts" > <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> - <th colspan="2"> - <h4>Alerts and Notices Setting</h4> - <em>Alerts sent to staff on ticket "events". Staff assignment takes precedence over team assignment.</em> + <th> + <h4>Alerts and Notices sent to staff on ticket "events"</h4> </th> </tr> </thead> <tbody> + <tr><th><em><b>New Ticket Alert</b>: Alert sent out on new tickets</em></th></tr> <tr> - <td width="160">New Ticket Alert:</td> - <td> + <td><em><b>Status:</b></em> <input type="radio" name="ticket_alert_active" value="1" <?php echo $config['ticket_alert_active']?'checked':''; ?> />Enable <input type="radio" name="ticket_alert_active" value="0" <?php echo !$config['ticket_alert_active']?'checked':''; ?> />Disable - <em>Alert sent out on new tickets <font class="error"> <?php echo $errors['ticket_alert_active']; ?></font></em><br> - <strong>Recipients</strong>: - <input type="checkbox" name="ticket_alert_admin" <?php echo $config['ticket_alert_admin']?'checked':''; ?>> Admin Email + <font class="error"> <?php echo $errors['ticket_alert_active']; ?></font></em> + </td> + </tr> + <tr> + <td> + <input type="checkbox" name="ticket_alert_admin" <?php echo $config['ticket_alert_admin']?'checked':''; ?>> Admin Email <em>(<?php echo $cfg->getAdminEmail(); ?>)</em> + </td> + </tr> + <tr> + <td> <input type="checkbox" name="ticket_alert_dept_manager" <?php echo $config['ticket_alert_dept_manager']?'checked':''; ?>> Department Manager - <input type="checkbox" name="ticket_alert_dept_members" <?php echo $config['ticket_alert_dept_members']?'checked':''; ?>> Department Members (spammy) </td> </tr> <tr> - <td width="160">New Message Alert:</td> <td> + <input type="checkbox" name="ticket_alert_dept_members" <?php echo $config['ticket_alert_dept_members']?'checked':''; ?>> Department Members <em>(spammy)</em> + </td> + </tr> + <tr><th><em><b>New Message Alert</b>: Alert sent out when a new message, from the user, is appended to an existing ticket</em></th></tr> + <tr> + <td><em><b>Status:</b></em> <input type="radio" name="message_alert_active" value="1" <?php echo $config['message_alert_active']?'checked':''; ?> />Enable + <input type="radio" name="message_alert_active" value="0" <?php echo !$config['message_alert_active']?'checked':''; ?> />Disable - <em>Alert sent out when a new message is appended to an existing ticket <font class="error"> <?php echo $errors['message_alert_active']; ?></font></em><br> - <strong>Recipients</strong>: + </td> + </tr> + <tr> + <td> <input type="checkbox" name="message_alert_laststaff" <?php echo $config['message_alert_laststaff']?'checked':''; ?>> Last Respondent + </td> + </tr> + <tr> + <td> <input type="checkbox" name="message_alert_assigned" <?php echo $config['message_alert_assigned']?'checked':''; ?>> Assigned Staff - <input type="checkbox" name="message_alert_dept_manager" <?php echo $config['message_alert_dept_manager']?'checked':''; ?>> Department Manager (spammy) </td> </tr> <tr> - <td width="160">New Internal Note Alert:</td> <td> + <input type="checkbox" name="message_alert_dept_manager" <?php echo $config['message_alert_dept_manager']?'checked':''; ?>> Department Manager <em>(spammy)</em> + </td> + </tr> + <tr><th><em><b>New Internal Note Alert</b>: Alert sent out when a new internal note is posted.</em></th></tr> + <tr> + <td><em><b>Status:</b></em> <input type="radio" name="note_alert_active" value="1" <?php echo $config['note_alert_active']?'checked':''; ?> />Enable + <input type="radio" name="note_alert_active" value="0" <?php echo !$config['note_alert_active']?'checked':''; ?> />Disable - <em>Alert sent out when a new internal note is posted <font class="error"> <?php echo $errors['note_alert_active']; ?></font></em><br> - <strong>Recipients</strong>: + <font class="error"> <?php echo $errors['note_alert_active']; ?></font> + </td> + </tr> + <tr> + <td> <input type="checkbox" name="note_alert_laststaff" <?php echo $config['note_alert_laststaff']?'checked':''; ?>> Last Respondent + </td> + </tr> + <tr> + <td> <input type="checkbox" name="note_alert_assigned" <?php echo $config['note_alert_assigned']?'checked':''; ?>> Assigned Staff - <input type="checkbox" name="note_alert_dept_manager" <?php echo $config['note_alert_dept_manager']?'checked':''; ?>> Department Manager (spammy) </td> </tr> <tr> - <td width="160">Ticket Assignment Alert:</td> <td> + <input type="checkbox" name="note_alert_dept_manager" <?php echo $config['note_alert_dept_manager']?'checked':''; ?>> Department Manager <em>(spammy)</em> + </td> + </tr> + <tr><th><em><b>Ticket Assignment Alert</b>: Alert sent out to staff on ticket assignment.</em></th></tr> + <tr> + <td><em><b>Status: </b></em> <input name="assigned_alert_active" value="1" checked="checked" type="radio">Enable + <input name="assigned_alert_active" value="0" type="radio">Disable - <em>Alert sent out to staff on ticket assignment <font class="error"> <?php echo $errors['assigned_alert_active']; ?></font></em><br> - <strong>Recipients</strong>: + <font class="error"> <?php echo $errors['assigned_alert_active']; ?></font> + </td> + </tr> + <tr> + <td> <input type="checkbox" name="assigned_alert_staff" <?php echo $config['assigned_alert_staff']?'checked':''; ?>> Assigned Staff - <input type="checkbox"name="assigned_alert_team_lead" <?php echo $config['assigned_alert_team_lead']?'checked':''; ?>>Team Lead (Team assignment) - <input type="checkbox"name="assigned_alert_team_members" <?php echo $config['assigned_alert_team_members']?'checked':''; ?>> - Team Members (spammy) </td> </tr> <tr> - <td width="160">Ticket Transfer Alert:</td> <td> + <input type="checkbox"name="assigned_alert_team_lead" <?php echo $config['assigned_alert_team_lead']?'checked':''; ?>>Team Lead <em>(On team assignment)</em> + </td> + </tr> + <tr> + <td> + <input type="checkbox"name="assigned_alert_team_members" <?php echo $config['assigned_alert_team_members']?'checked':''; ?>> + Team Members <em>(spammy)</em> + </td> + </tr> + <tr><th><em><b>Ticket Transfer Alert</b>: Alert sent out to staff of the target department on ticket transfer.</em></th></tr> + <tr> + <td><em><b>Status:</b></em> <input type="radio" name="transfer_alert_active" value="1" <?php echo $config['transfer_alert_active']?'checked':''; ?> />Enable <input type="radio" name="transfer_alert_active" value="0" <?php echo !$config['transfer_alert_active']?'checked':''; ?> />Disable - <em>Alert sent out to staff on ticket transfer <font class="error"> -<?php echo $errors['alert_alert_active']; ?></font></em><br> - <strong>Recipients</strong>: + <font class="error"> <?php echo $errors['alert_alert_active']; ?></font> + </td> + </tr> + <tr> + <td> <input type="checkbox" name="transfer_alert_assigned" <?php echo $config['transfer_alert_assigned']?'checked':''; ?>> Assigned Staff/Team + </td> + </tr> + <tr> + <td> <input type="checkbox" name="transfer_alert_dept_manager" <?php echo $config['transfer_alert_dept_manager']?'checked':''; ?>> Department Manager - <input type="checkbox" name="transfer_alert_dept_members" <?php echo $config['transfer_alert_dept_members']?'checked':''; ?>> Department Members - (spammy) </td> </tr> <tr> - <td width="160">Overdue Ticket Alert:</td> <td> + <input type="checkbox" name="transfer_alert_dept_members" <?php echo $config['transfer_alert_dept_members']?'checked':''; ?>> + Department Members <em>(spammy)</em> + </td> + </tr> + <tr><th><em><b>Overdue Ticket Alert</b>: Alert sent out when a ticket becomes overdue - admin email gets an alert by default.</em></th></tr> + <tr> + <td><em><b>Status:</b></em> <input type="radio" name="overdue_alert_active" value="1" <?php echo $config['overdue_alert_active']?'checked':''; ?> />Enable <input type="radio" name="overdue_alert_active" value="0" <?php echo !$config['overdue_alert_active']?'checked':''; ?> />Disable - <em>Alert sent out when a ticket becomes overdue - admin email gets an alert by default. <font class="error"> <?php echo $errors['overdue_alert_active']; ?></font></em><br> - <strong>Recipients</strong>: + <font class="error"> <?php echo $errors['overdue_alert_active']; ?></font> + </td> + </tr> + <tr> + <td> <input type="checkbox" name="overdue_alert_assigned" <?php echo $config['overdue_alert_assigned']?'checked':''; ?>> Assigned Staff/Team + </td> + </tr> + <tr> + <td> <input type="checkbox" name="overdue_alert_dept_manager" <?php echo $config['overdue_alert_dept_manager']?'checked':''; ?>> Department Manager - <input type="checkbox" name="overdue_alert_dept_members" <?php echo $config['overdue_alert_dept_members']?'checked':''; ?>> Department Members (spammy) </td> </tr> <tr> - <td width="160">System Alerts:</td> - <td><em><b>Enabled</b>: Errors are sent to system admin email (<?php echo $cfg->getAdminEmail(); ?>)</em><br> + <td> + <input type="checkbox" name="overdue_alert_dept_members" <?php echo $config['overdue_alert_dept_members']?'checked':''; ?>> Department Members <em>(spammy)</em> + </td> + </tr> + <tr><th><em><b>System Alerts</b>: Enabled by default. Errors are sent to system admin email (<?php echo $cfg->getAdminEmail(); ?>)</em></th></tr> + <tr> + <td> <input type="checkbox" name="send_sys_errors" checked="checked" disabled="disabled">System Errors + </td> + </tr> + <tr> + <td> <input type="checkbox" name="send_sql_errors" <?php echo $config['send_sql_errors']?'checked':''; ?>>SQL errors + </td> + </tr> + <tr> + <td> <input type="checkbox" name="send_login_errors" <?php echo $config['send_login_errors']?'checked':''; ?>>Excessive Login attempts </td> </tr> </tbody> </table> -<p style="padding-left:200px;"> +<p style="padding-left:350px;"> <input class="button" type="submit" name="submit" value="Save Changes"> <input class="button" type="reset" name="reset" value="Reset Changes"> </p> diff --git a/include/staff/settings-attachments.inc.php b/include/staff/settings-attachments.inc.php deleted file mode 100644 index b381fa40d334f9f2c9077f91698c6ec90aa31491..0000000000000000000000000000000000000000 --- a/include/staff/settings-attachments.inc.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php -if(!($maxfileuploads=ini_get('max_file_uploads'))) - $maxfileuploads=DEFAULT_MAX_FILE_UPLOADS; - -?> -<form action="settings.php?t=attachments" method="post" id="save"> -<?php csrf_token(); ?> -<input type="hidden" name="t" value="attachments" > -<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> - <thead> - <tr> - <th colspan="2"> - <h4>Attachments Settings</h4> - <em> Before enabling attachments make sure you understand PHP file upload settings and security issues related to file upload.</em> - </th> - </tr> - </thead> - <tbody> - <tr> - <td width="180">Allow Attachments:</td> - <td> - <input type="checkbox" name="allow_attachments" <?php echo $config['allow_attachments']?'checked="checked"':''; ?>><b>Allow Attachments</b> - <em>(Global Setting)</em> - <font class="error"> <?php echo $errors['allow_attachments']; ?></font> - </td> - </tr> - <tr> - <td width="180">Emailed Attachments:</td> - <td> - <input type="checkbox" name="allow_email_attachments" <?php echo $config['allow_email_attachments']?'checked="checked"':''; ?>> Accept emailed files - <font class="error"> <?php echo $errors['allow_email_attachments']; ?></font> - </td> - </tr> - <tr> - <td width="180">Online Attachments:</td> - <td> - <input type="checkbox" name="allow_online_attachments" <?php echo $config['allow_online_attachments']?'checked="checked"':''; ?> > - Allow web upload - <input type="checkbox" name="allow_online_attachments_onlogin" <?php echo $config['allow_online_attachments_onlogin'] ?'checked="checked"':''; ?> > - Limit to authenticated users only. <em>(User must be logged in to upload files)</em> - <font class="error"> <?php echo $errors['allow_online_attachments']; ?></font> - </td> - </tr> - <tr> - <td>Max. User File Uploads:</td> - <td> - <select name="max_user_file_uploads"> - <?php - for($i = 1; $i <=$maxfileuploads; $i++) { - ?> - <option <?php echo $config['max_user_file_uploads']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>"> - <?php echo $i; ?> <?php echo ($i>1)?'files':'file'; ?></option> - <?php - } ?> - </select> - <em>(Number of files the user is allowed to upload simultaneously)</em> - <font class="error"> <?php echo $errors['max_user_file_uploads']; ?></font> - </td> - </tr> - <tr> - <td>Max. Staff File Uploads:</td> - <td> - <select name="max_staff_file_uploads"> - <?php - for($i = 1; $i <=$maxfileuploads; $i++) { - ?> - <option <?php echo $config['max_staff_file_uploads']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>"> - <?php echo $i; ?> <?php echo ($i>1)?'files':'file'; ?></option> - <?php - } ?> - </select> - <em>(Number of files the staff is allowed to upload simultaneously)</em> - <font class="error"> <?php echo $errors['max_staff_file_uploads']; ?></font> - </td> - </tr> - <tr> - <td width="180">Maximum File Size:</td> - <td> - <input type="text" name="max_file_size" value="<?php echo $config['max_file_size']; ?>"> in bytes. - <em>(Max <?php echo Format::file_size(ini_get('upload_max_filesize')); ?>)</em> - <font class="error"> <?php echo $errors['max_file_size']; ?></font> - </td> - </tr> - <tr> - <td width="180">Ticket Response Files:</td> - <td> - <input type="checkbox" name="email_attachments" <?php echo $config['email_attachments']?'checked="checked"':''; ?> >Email attachments to the user - </td> - </tr> - <tr> - <th colspan="2"> - <em><strong>Accepted File Types</strong>: Limit the type of files users are allowed to upload. - <font class="error"> <?php echo $errors['allowed_filetypes']; ?></font></em> - </th> - </tr> - <tr> - <td colspan="2"> - <em>Enter allowed file extensions separated by a comma. e.g .doc, .pdf. To accept all files enter wildcard <b><i>.*</i></b> i.e dotStar (NOT Recommended).</em><br> - <textarea name="allowed_filetypes" cols="21" rows="4" style="width: 65%;" wrap="hard" ><?php echo $config['allowed_filetypes']; ?></textarea> - </td> - </tr> - </tbody> -</table> -<p style="padding-left:210px;"> - <input class="button" type="submit" name="submit" value="Save Changes"> - <input class="button" type="reset" name="reset" value="Reset Changes"> -</p> -</form> diff --git a/include/staff/settings-autoresponders.inc.php b/include/staff/settings-autoresp.inc.php similarity index 93% rename from include/staff/settings-autoresponders.inc.php rename to include/staff/settings-autoresp.inc.php index 106e7f3f4d7063a6bcb4835cdbd07006b6f16b5c..b5815915fc346bb97fbfa318db9416bda6bfccce 100644 --- a/include/staff/settings-autoresponders.inc.php +++ b/include/staff/settings-autoresp.inc.php @@ -1,6 +1,7 @@ -<form action="settings.php?t=autoresponders" method="post" id="save"> +<h2>Autoresponder Settings</h2> +<form action="settings.php?t=autoresp" method="post" id="save"> <?php csrf_token(); ?> -<input type="hidden" name="t" value="autoresponders" > +<input type="hidden" name="t" value="autoresp" > <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> diff --git a/include/staff/settings-dates.inc.php b/include/staff/settings-dates.inc.php deleted file mode 100644 index f8085cfc3193342bf1bd6c66df87b48d9dcf5edd..0000000000000000000000000000000000000000 --- a/include/staff/settings-dates.inc.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -$gmtime=Misc::gmtime(); -?> -<form action="settings.php?t=dates" method="post" id="save"> -<?php csrf_token(); ?> -<input type="hidden" name="t" value="dates" > -<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> - <thead> - <tr> - <th colspan="2"> - <h4>Date and Time Options</h4> - <em>Please refer to <a href="http://php.net/date" target="_blank">PHP Manual</a> for supported parameters.</em> - </th> - </tr> - </thead> - <tbody> - <tr><td width="220" class="required">Time Format:</td> - <td> - <input type="text" name="time_format" value="<?php echo $config['time_format']; ?>"> - <font class="error">* <?php echo $errors['time_format']; ?></font> - <em><?php echo Format::date($config['time_format'],$gmtime,$config['timezone_offset'],$config['enable_daylight_saving']); ?></em></td> - </tr> - <tr><td width="220" class="required">Date Format:</td> - <td><input type="text" name="date_format" value="<?php echo $config['date_format']; ?>"> - <font class="error">* <?php echo $errors['date_format']; ?></font> - <em><?php echo Format::date($config['date_format'],$gmtime,$config['timezone_offset'],$config['enable_daylight_saving']); ?></em> - </td> - </tr> - <tr><td width="220" class="required">Date & Time Format:</td> - <td><input type="text" name="datetime_format" value="<?php echo $config['datetime_format']; ?>"> - <font class="error">* <?php echo $errors['datetime_format']; ?></font> - <em><?php echo Format::date($config['datetime_format'],$gmtime,$config['timezone_offset'],$config['enable_daylight_saving']); ?></em> - </td> - </tr> - <tr><td width="220" class="required">Day, Date & Time Format:</td> - <td><input type="text" name="daydatetime_format" value="<?php echo $config['daydatetime_format']; ?>"> - <font class="error">* <?php echo $errors['daydatetime_format']; ?></font> - <em><?php echo Format::date($config['daydatetime_format'],$gmtime,$config['timezone_offset'],$config['enable_daylight_saving']); ?></em> - </td> - </tr> - <tr><td width="220" class="required">Default Time Zone:</td> - <td> - <select name="default_timezone_id"> - <option value="">— Select Default Time Zone —</option> - <?php - $sql='SELECT id, offset,timezone FROM '.TIMEZONE_TABLE.' ORDER BY id'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$offset, $tz)=db_fetch_row($res)){ - $sel=($config['default_timezone_id']==$id)?'selected="selected"':''; - echo sprintf('<option value="%d" %s>GMT %s - %s</option>',$id,$sel,$offset,$tz); - } - } - ?> - </select> - <font class="error">* <?php echo $errors['default_timezone_id']; ?></font> - </td> - </tr> - <tr> - <td width="220">Daylight Saving:</td> - <td> - <input type="checkbox" name="enable_daylight_saving" <?php echo $config['enable_daylight_saving'] ? 'checked="checked"': ''; ?>>Observe daylight savings - </td> - </tr> - </tbody> -</table> -<p style="padding-left:250px;"> - <input class="button" type="submit" name="submit" value="Save Changes"> - <input class="button" type="reset" name="reset" value="Reset Changes"> -</p> -</form> diff --git a/include/staff/settings-emails.inc.php b/include/staff/settings-emails.inc.php index e4ccaf3a4e69329c15d474bfd3f136f9a8fe17f9..f8674095d49cfe25102ef682e64bb9df59e1614a 100644 --- a/include/staff/settings-emails.inc.php +++ b/include/staff/settings-emails.inc.php @@ -1,110 +1,118 @@ -<form action="settings.php?t=emails" method="post" id="save"> +<?php +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied'); +?> +<h2>Email Settings and Options</h2> +<form action="settings.php?t=emails" method="post" id="save"> <?php csrf_token(); ?> -<input type="hidden" name="t" value="emails" > -<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> - <thead> - <tr> - <th colspan="2"> - <h4>Email Settings</h4> - <em>Note that some of the global settings can be overwritten at department/email level.</em> - </th> - </tr> - </thead> - <tbody> - <tr> - <td width="180" class="required">Default System Email:</td> - <td> - <select name="default_email_id"> - <option value=0 disabled>Select One</option> +<input type="hidden" name="t" value="emails" > +<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th colspan="2"> + <h4>Email Settings</h4> + <em>Note that some of the global settings can be overwritten at department/email level.</em> + </th> + </tr> + </thead> + <tbody> + <tr> + <td width="180" class="required">Default System Email:</td> + <td> + <select name="default_email_id"> + <option value=0 disabled>Select One</option> <?php - $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE; - if(($res=db_query($sql)) && db_num_rows($res)){ - while (list($id,$email,$name) = db_fetch_row($res)){ - $email=$name?"$name <$email>":$email; - ?> - <option value="<?php echo $id; ?>"<?php echo ($config['default_email_id']==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option> + $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE; + if(($res=db_query($sql)) && db_num_rows($res)){ + while (list($id,$email,$name) = db_fetch_row($res)){ + $email=$name?"$name <$email>":$email; + ?> + <option value="<?php echo $id; ?>"<?php echo ($config['default_email_id']==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option> <?php - } - } ?> - </select> - <font class="error">* <?php echo $errors['default_email_id']; ?></font> - </td> - </tr> - <tr> - <td width="180" class="required">Default Alert Email:</td> - <td> - <select name="alert_email_id"> - <option value="0" selected="selected">Use Default System Email (above)</option> + } + } ?> + </select> + <font class="error">* <?php echo $errors['default_email_id']; ?></font> + </td> + </tr> + <tr> + <td width="180" class="required">Default Alert Email:</td> + <td> + <select name="alert_email_id"> + <option value="0" selected="selected">Use Default System Email (above)</option> <?php - $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' WHERE email_id != '.db_input($config['default_email_id']); - if(($res=db_query($sql)) && db_num_rows($res)){ - while (list($id,$email,$name) = db_fetch_row($res)){ - $email=$name?"$name <$email>":$email; - ?> - <option value="<?php echo $id; ?>"<?php echo ($config['alert_email_id']==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option> + $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' WHERE email_id != '.db_input($config['default_email_id']); + if(($res=db_query($sql)) && db_num_rows($res)){ + while (list($id,$email,$name) = db_fetch_row($res)){ + $email=$name?"$name <$email>":$email; + ?> + <option value="<?php echo $id; ?>"<?php echo ($config['alert_email_id']==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option> <?php - } - } ?> - </select> - <font class="error">* <?php echo $errors['alert_email_id']; ?></font> - </td> - </tr> - <tr> - <td width="180" class="required">Admin Email Address:</td> - <td> - <input type="text" size=40 name="admin_email" value="<?php echo $config['admin_email']; ?>"> - <font class="error">* <?php echo $errors['admin_email']; ?></font></td> - </tr> - <tr><th colspan=2><em><strong>Incoming Emails</strong>: For mail fetcher (POP/IMAP) to work you must set a cron job or enable auto-cron</em></th> - <tr> - <td width="180">Email Polling:</td> - <td><input type="checkbox" name="enable_mail_polling" value=1 <?php echo $config['enable_mail_polling']? 'checked="checked"': ''; ?> > Enable POP/IMAP - <em>(Global setting which can be disabled at email level)</em> - </td> - </tr> - <tr> - <td width="180">Email Piping:</td> - <td><input type="checkbox" name="enable_email_piping" value=1 <?php echo $config['enable_email_piping']? 'checked="checked"': ''; ?>> Enable email piping - <em>(You pipe we accept policy)</em> - </td> - </tr> - <tr> - <td width="180">Strip Quoted Reply:</td> - <td> - <input type="checkbox" name="strip_quoted_reply" <?php echo $config['strip_quoted_reply'] ? 'checked="checked"':''; ?>> - <em>(depends on the reply separator tag set below)</em> - <font class="error"> <?php echo $errors['strip_quoted_reply']; ?></font> - </td> - </tr> - <tr> - <td width="180">Reply Separator Tag:</td> - <td><input type="text" name="reply_separator" value="<?php echo $config['reply_separator']; ?>"> - <font class="error"> <?php echo $errors['reply_separator']; ?></font> - </td> - </tr> - <tr><th colspan=2><em><strong>Outgoing Emails</strong>: Default email only applies to outgoing emails without SMTP setting.</em></th></tr> - <tr><td width="180">Default Outgoing Email:</td> - <td> - <select name="default_smtp_id"> - <option value=0 selected="selected">None: Use PHP mail function</option> + } + } ?> + </select> + <font class="error">* <?php echo $errors['alert_email_id']; ?></font> + </td> + </tr> + <tr> + <td width="180" class="required">Admin's Email Address:</td> + <td> + <input type="text" size=40 name="admin_email" value="<?php echo $config['admin_email']; ?>"> + <font class="error">* <?php echo $errors['admin_email']; ?></font> + <em>(System administrator's email)</em> + </td> + </tr> + <tr><th colspan=2><em><strong>Incoming Emails</strong>: For mail fetcher (polling) to work you must set an external cron job or enable auto-cron</em></th> + <tr> + <td width="180">Email Polling:</td> + <td><input type="checkbox" name="enable_mail_polling" value=1 <?php echo $config['enable_mail_polling']? 'checked="checked"': ''; ?> > Enable POP/IMAP polling + + <input type="checkbox" name="enable_auto_cron" <?php echo $config['enable_auto_cron']?'checked="checked"':''; ?>> + Enable Auto-Cron <em>(Poll based on staff activity - NOT recommended)</em> + </td> + </tr> + <tr> + <td width="180">Email Piping:</td> + <td><input type="checkbox" name="enable_email_piping" value=1 <?php echo $config['enable_email_piping']? 'checked="checked"': ''; ?>> Enable email piping + <em>(You pipe we accept policy)</em> + </td> + </tr> + <tr> + <td width="180">Strip Quoted Reply:</td> + <td> + <input type="checkbox" name="strip_quoted_reply" <?php echo $config['strip_quoted_reply'] ? 'checked="checked"':''; ?>> + <em>(depends on the reply separator tag set below)</em> + <font class="error"> <?php echo $errors['strip_quoted_reply']; ?></font> + </td> + </tr> + <tr> + <td width="180">Reply Separator Tag:</td> + <td><input type="text" name="reply_separator" value="<?php echo $config['reply_separator']; ?>"> + <font class="error"> <?php echo $errors['reply_separator']; ?></font> + </td> + </tr> + <tr><th colspan=2><em><strong>Outgoing Emails</strong>: Default email only applies to outgoing emails without SMTP setting.</em></th></tr> + <tr><td width="180">Default Outgoing Email:</td> + <td> + <select name="default_smtp_id"> + <option value=0 selected="selected">None: Use PHP mail function</option> <?php - $sql='SELECT email_id,email,name,smtp_host FROM '.EMAIL_TABLE.' WHERE smtp_active=1'; - - if(($res=db_query($sql)) && db_num_rows($res)) { - while (list($id,$email,$name,$host) = db_fetch_row($res)){ - $email=$name?"$name <$email>":$email; - ?> - <option value="<?php echo $id; ?>"<?php echo ($config['default_smtp_id']==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option> + $sql='SELECT email_id,email,name,smtp_host FROM '.EMAIL_TABLE.' WHERE smtp_active=1'; + + if(($res=db_query($sql)) && db_num_rows($res)) { + while (list($id,$email,$name,$host) = db_fetch_row($res)){ + $email=$name?"$name <$email>":$email; + ?> + <option value="<?php echo $id; ?>"<?php echo ($config['default_smtp_id']==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option> <?php - } - } ?> - </select> <font class="error"> <?php echo $errors['default_smtp_id']; ?></font> - </td> - </tr> - </tbody> -</table> -<p style="padding-left:250px;"> - <input class="button" type="submit" name="submit" value="Save Changes"> - <input class="button" type="reset" name="reset" value="Reset Changes"> -</p> -</form> + } + } ?> + </select> <font class="error"> <?php echo $errors['default_smtp_id']; ?></font> + </td> + </tr> + </tbody> +</table> +<p style="padding-left:250px;"> + <input class="button" type="submit" name="submit" value="Save Changes"> + <input class="button" type="reset" name="reset" value="Reset Changes"> +</p> +</form> diff --git a/include/staff/settings-kb.inc.php b/include/staff/settings-kb.inc.php index 6fe8433f5ee580c5a201a42518652106af63d313..0ab2ec09449a07f8f7efa78f8602683534b7666a 100644 --- a/include/staff/settings-kb.inc.php +++ b/include/staff/settings-kb.inc.php @@ -1,3 +1,7 @@ +<?php +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied'); +?> +<h2>Knowledge Base Settings and Options</h2> <form action="settings.php?t=kb" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="t" value="kb" > @@ -5,25 +9,25 @@ <thead> <tr> <th colspan="2"> - <h4>Knowledgebase Settings</h4> - <em>Disabling knowledgebase disables user's knowledgebase interface.</em> + <h4>Knowledge Base Settings</h4> + <em>Disabling knowledge base disables clients'interface.</em> </th> </tr> </thead> <tbody> <tr> - <td width="180">Knowledgebase Status:</td> + <td width="180">Knowledge base status:</td> <td> <input type="checkbox" name="enable_kb" value="1" <?php echo $config['enable_kb']?'checked="checked"':''; ?>> - Enable Knowledgebase <em>(Client Interface)</em> + Enable Knowledge base <em>(Client interface)</em> <font class="error"> <?php echo $errors['enable_kb']; ?></font> </td> </tr> <tr> - <td width="180">Premade Responses:</td> + <td width="180">Canned Responses:</td> <td> <input type="checkbox" name="enable_premade" value="1" <?php echo $config['enable_premade']?'checked="checked"':''; ?> > - Enable premade/canned responses <em>(Available on ticket reply)</em> + Enable canned responses <em>(Available on ticket reply)</em> <font class="error"> <?php echo $errors['enable_premade']; ?></font> </td> </tr> diff --git a/include/staff/settings-general.inc.php b/include/staff/settings-system.inc.php similarity index 66% rename from include/staff/settings-general.inc.php rename to include/staff/settings-system.inc.php index 20bbd94866960d2e06ae4d3451825d5752c853ac..3fba7f15977390d6882f00894523b90d92d4aced 100644 --- a/include/staff/settings-general.inc.php +++ b/include/staff/settings-system.inc.php @@ -1,12 +1,18 @@ -<form action="settings.php?t=general" method="post" id="save"> +<?php +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied'); + +$gmtime = Misc::gmtime(); +?> +<h2>System Settings and Preferences - <span>osTicket (v<?php echo $cfg->getVersion(); ?>)</span></h2> +<form action="settings.php?t=system" method="post" id="save"> <?php csrf_token(); ?> -<input type="hidden" name="t" value="general" > +<input type="hidden" name="t" value="system" > <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> <th colspan="2"> - <h4>General Settings</h4> - <em>Offline mode will disable client interface and only allow admins to login to Staff Control Panel</em> + <h4>System Settings & Preferences</h4> + <em><b>General Settings</b>: Offline mode will disable client interface and only allow admins to login to Staff Control Panel</em> </th> </tr> </thead> @@ -39,7 +45,7 @@ <?php $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' WHERE ispublic=1'; if(($res=db_query($sql)) && db_num_rows($res)){ - while (list($id,$name) = db_fetch_row($res)){ + while (list($id, $name) = db_fetch_row($res)){ $selected = ($config['default_dept_id']==$id)?'selected="selected"':''; ?> <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?> Dept</option> <?php @@ -56,7 +62,7 @@ <?php $sql='SELECT tpl_id,name FROM '.EMAIL_TEMPLATE_TABLE.' WHERE isactive=1 AND cfg_id='.db_input($cfg->getId()).' ORDER BY name'; if(($res=db_query($sql)) && db_num_rows($res)){ - while (list($id,$name) = db_fetch_row($res)){ + while (list($id, $name) = db_fetch_row($res)){ $selected = ($config['default_template_id']==$id)?'selected="selected"':''; ?> <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?></option> <?php @@ -113,26 +119,32 @@ <?php for ($i = 1; $i <= 12; $i++) { echo sprintf('<option value="%d" %s>%s%s</option>', - $i,(($config['passwd_reset_period']==$i)?'selected="selected"':''),$i>1?"Every $i ":'',$i>1?' Months':'Monthly'); + $i,(($config['passwd_reset_period']==$i)?'selected="selected"':''), $i>1?"Every $i ":'', $i>1?' Months':'Monthly'); } ?> </select> <font class="error"> <?php echo $errors['passwd_reset_period']; ?></font> </td> </tr> + <tr><td>Bind Staff Session to IP:</td> + <td> + <input type="checkbox" name="staff_ip_binding" <?php echo $config['staff_ip_binding']?'checked="checked"':''; ?>> + <em>(binds staff session to originating IP address upon login)</em> + </td> + </tr> <tr><td>Staff Excessive Logins:</td> <td> <select name="staff_max_logins"> <?php for ($i = 1; $i <= 10; $i++) { - echo sprintf('<option value="%d" %s>%d</option>',$i,(($config['staff_max_logins']==$i)?'selected="selected"':''),$i); + echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['staff_max_logins']==$i)?'selected="selected"':''), $i); } ?> </select> failed login attempt(s) allowed before a <select name="staff_login_timeout"> <?php for ($i = 1; $i <= 10; $i++) { - echo sprintf('<option value="%d" %s>%d</option>',$i,(($config['staff_login_timeout']==$i)?'selected="selected"':''),$i); + echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['staff_login_timeout']==$i)?'selected="selected"':''), $i); } ?> </select> minute lock-out is enforced. @@ -144,18 +156,12 @@ Maximum idle time in minutes before a staff member must log in again (enter 0 to disable). </td> </tr> - <tr><td>Bind Staff Session to IP:</td> - <td> - <input type="checkbox" name="staff_ip_binding" <?php echo $config['staff_ip_binding']?'checked="checked"':''; ?>> - <em>(binds staff session to originating IP address upon login)</em> - </td> - </tr> <tr><td>Client Excessive Logins:</td> <td> <select name="client_max_logins"> <?php for ($i = 1; $i <= 10; $i++) { - echo sprintf('<option value="%d" %s>%d</option>',$i,(($config['client_max_logins']==$i)?'selected="selected"':''),$i); + echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['client_max_logins']==$i)?'selected="selected"':''), $i); } ?> @@ -163,7 +169,7 @@ <select name="client_login_timeout"> <?php for ($i = 1; $i <= 10; $i++) { - echo sprintf('<option value="%d" %s>%d</option>',$i,(($config['client_login_timeout']==$i)?'selected="selected"':''),$i); + echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['client_login_timeout']==$i)?'selected="selected"':''), $i); } ?> </select> minute lock-out is enforced. @@ -176,16 +182,56 @@ Maximum idle time in minutes before a client must log in again (enter 0 to disable). </td> </tr> - <tr><td>Clickable URLs:</td> + <tr> + <th colspan="2"> + <em><b>Date and Time Options</b>: Please refer to <a href="http://php.net/date" target="_blank">PHP Manual</a> for supported parameters.</em> + </th> + </tr> + <tr><td width="220" class="required">Time Format:</td> + <td> + <input type="text" name="time_format" value="<?php echo $config['time_format']; ?>"> + <font class="error">* <?php echo $errors['time_format']; ?></font> + <em><?php echo Format::date($config['time_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em></td> + </tr> + <tr><td width="220" class="required">Date Format:</td> + <td><input type="text" name="date_format" value="<?php echo $config['date_format']; ?>"> + <font class="error">* <?php echo $errors['date_format']; ?></font> + <em><?php echo Format::date($config['date_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em> + </td> + </tr> + <tr><td width="220" class="required">Date & Time Format:</td> + <td><input type="text" name="datetime_format" value="<?php echo $config['datetime_format']; ?>"> + <font class="error">* <?php echo $errors['datetime_format']; ?></font> + <em><?php echo Format::date($config['datetime_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em> + </td> + </tr> + <tr><td width="220" class="required">Day, Date & Time Format:</td> + <td><input type="text" name="daydatetime_format" value="<?php echo $config['daydatetime_format']; ?>"> + <font class="error">* <?php echo $errors['daydatetime_format']; ?></font> + <em><?php echo Format::date($config['daydatetime_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em> + </td> + </tr> + <tr><td width="220" class="required">Default Time Zone:</td> <td> - <input type="checkbox" name="clickable_urls" <?php echo $config['clickable_urls']?'checked="checked"':''; ?>> - <em>(converts URLs in messages to clickable links)</em> + <select name="default_timezone_id"> + <option value="">— Select Default Time Zone —</option> + <?php + $sql='SELECT id, offset,timezone FROM '.TIMEZONE_TABLE.' ORDER BY id'; + if(($res=db_query($sql)) && db_num_rows($res)){ + while(list($id, $offset, $tz)=db_fetch_row($res)){ + $sel=($config['default_timezone_id']==$id)?'selected="selected"':''; + echo sprintf('<option value="%d" %s>GMT %s - %s</option>', $id, $sel, $offset, $tz); + } + } + ?> + </select> + <font class="error">* <?php echo $errors['default_timezone_id']; ?></font> </td> </tr> - <tr><td>Enable Auto Cron:</td> + <tr> + <td width="220">Daylight Saving:</td> <td> - <input type="checkbox" name="enable_auto_cron" <?php echo $config['enable_auto_cron']?'checked="checked"':''; ?>> - <em>(executes cron jobs based on staff activity - not recommended)</em> + <input type="checkbox" name="enable_daylight_saving" <?php echo $config['enable_daylight_saving'] ? 'checked="checked"': ''; ?>>Observe daylight savings </td> </tr> </tbody> @@ -195,4 +241,3 @@ <input class="button" type="reset" name="reset" value="Reset Changes"> </p> </form> - diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index 280abca08ea44d698e0a41b56b1d8d0a62cd1693..992e9900425c19d708f7cb35d75a59f7b18c36bf 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -1,3 +1,9 @@ +<?php +if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied'); +if(!($maxfileuploads=ini_get('max_file_uploads'))) + $maxfileuploads=DEFAULT_MAX_FILE_UPLOADS; +?> +<h2>Ticket Settings and Options</h2> <form action="settings.php?t=tickets" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="t" value="tickets" > @@ -5,7 +11,7 @@ <thead> <tr> <th colspan="2"> - <h4>Ticket Settings and Options</h4> + <h4>Ticket Settings</h4> <em>Global ticket settings and options.</em> </th> </tr> @@ -55,53 +61,59 @@ </td> </tr> <tr> - <td width="180">Web Tickets Priority</td> + <td>Maximum <b>Open</b> Tickets:</td> <td> - <input type="checkbox" name="allow_priority_change" value="1" <?php echo $config['allow_priority_change'] ?'checked="checked"':''; ?>> - <em>(Allow user to overwrite/set priority)</em> + <input type="text" name="max_open_tickets" size=4 value="<?php echo $config['max_open_tickets']; ?>"> + per email/user. <em>(Helps with spam and email flood control - enter 0 for unlimited)</em> </td> </tr> <tr> - <td width="180">Emailed Tickets Priority</td> + <td>Ticket Auto-lock Time:</td> <td> - <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?> > - <em>(Use email priority when available)</em> + <input type="text" name="autolock_minutes" size=4 value="<?php echo $config['autolock_minutes']; ?>"> + <font class="error"><?php echo $errors['autolock_minutes']; ?></font> + <em>(Minutes to lock a ticket on activity - enter 0 to disable locking)</em> + </td> + </tr> + <tr> + <td width="180">Web Tickets Priority:</td> + <td> + <input type="checkbox" name="allow_priority_change" value="1" <?php echo $config['allow_priority_change'] ?'checked="checked"':''; ?>> + <em>(Allow user to overwrite/set priority)</em> + </td> + </tr> + <tr> + <td width="180">Emailed Tickets Priority:</td> + <td> + <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?> > + <em>(Use email priority when available)</em> </td> </tr> <tr> - <td width="180">Show Related Tickets</td> + <td width="180">Show Related Tickets:</td> <td> <input type="checkbox" name="show_related_tickets" value="1" <?php echo $config['show_related_tickets'] ?'checked="checked"':''; ?> > <em>(Show all related tickets on user login - otherwise access is restricted to one ticket view per login)</em> </td> </tr> <tr> - <td width="180">Show Notes Inline</td> + <td width="180">Show Notes Inline:</td> <td> <input type="checkbox" name="show_notes_inline" value="1" <?php echo $config['show_notes_inline'] ?'checked="checked"':''; ?> > <em>(Show internal notes inline)</em> </td> - </tr> - <tr> - <td>Human Verification:</td> - <td> - <input type="checkbox" name="enable_captcha" <?php echo $config['enable_captcha']?'checked="checked"':''; ?>> - Enable CAPTCHA on new web tickets.<em>(requires GDLib)</em> <font class="error"> <?php echo $errors['enable_captcha']; ?></font><br/> - </td> </tr> - <tr> - <td>Maximum <b>Open</b> Tickets:</td> + <tr><td>Clickable URLs:</td> <td> - <input type="text" name="max_open_tickets" size=4 value="<?php echo $config['max_open_tickets']; ?>"> - per email/user. <em>(Helps with spam and email flood control - enter 0 for unlimited)</em> + <input type="checkbox" name="clickable_urls" <?php echo $config['clickable_urls']?'checked="checked"':''; ?>> + <em>(converts URLs in ticket thread to clickable links)</em> </td> </tr> <tr> - <td>Ticket Auto-lock Time:</td> + <td>Human Verification:</td> <td> - <input type="text" name="autolock_minutes" size=4 value="<?php echo $config['autolock_minutes']; ?>"> - <font class="error"><?php echo $errors['autolock_minutes']; ?></font> - <em>(Minutes to lock a ticket on activity - enter 0 to disable locking)</em> + <input type="checkbox" name="enable_captcha" <?php echo $config['enable_captcha']?'checked="checked"':''; ?>> + Enable CAPTCHA on new web tickets.<em>(requires GDLib)</em> <font class="error"> <?php echo $errors['enable_captcha']; ?></font><br/> </td> </tr> <tr> @@ -139,6 +151,94 @@ Hide staff's name on responses. </td> </tr> + <tr> + <th colspan="2"> + <em><b>Attachments</b>: Size setting mainly apply to web tickets.</em> + </th> + </tr> + <tr> + <td width="180">Allow Attachments:</td> + <td> + <input type="checkbox" name="allow_attachments" <?php echo $config['allow_attachments']?'checked="checked"':''; ?>><b>Allow Attachments</b> + <em>(Global Setting)</em> + <font class="error"> <?php echo $errors['allow_attachments']; ?></font> + </td> + </tr> + <tr> + <td width="180">Emailed Attachments:</td> + <td> + <input type="checkbox" name="allow_email_attachments" <?php echo $config['allow_email_attachments']?'checked="checked"':''; ?>> Accept emailed files + <font class="error"> <?php echo $errors['allow_email_attachments']; ?></font> + </td> + </tr> + <tr> + <td width="180">Online Attachments:</td> + <td> + <input type="checkbox" name="allow_online_attachments" <?php echo $config['allow_online_attachments']?'checked="checked"':''; ?> > + Allow web upload + <input type="checkbox" name="allow_online_attachments_onlogin" <?php echo $config['allow_online_attachments_onlogin'] ?'checked="checked"':''; ?> > + Limit to authenticated users only. <em>(User must be logged in to upload files)</em> + <font class="error"> <?php echo $errors['allow_online_attachments']; ?></font> + </td> + </tr> + <tr> + <td>Max. User File Uploads:</td> + <td> + <select name="max_user_file_uploads"> + <?php + for($i = 1; $i <=$maxfileuploads; $i++) { + ?> + <option <?php echo $config['max_user_file_uploads']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>"> + <?php echo $i; ?> <?php echo ($i>1)?'files':'file'; ?></option> + <?php + } ?> + </select> + <em>(Number of files the user is allowed to upload simultaneously)</em> + <font class="error"> <?php echo $errors['max_user_file_uploads']; ?></font> + </td> + </tr> + <tr> + <td>Max. Staff File Uploads:</td> + <td> + <select name="max_staff_file_uploads"> + <?php + for($i = 1; $i <=$maxfileuploads; $i++) { + ?> + <option <?php echo $config['max_staff_file_uploads']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>"> + <?php echo $i; ?> <?php echo ($i>1)?'files':'file'; ?></option> + <?php + } ?> + </select> + <em>(Number of files the staff is allowed to upload simultaneously)</em> + <font class="error"> <?php echo $errors['max_staff_file_uploads']; ?></font> + </td> + </tr> + <tr> + <td width="180">Maximum File Size:</td> + <td> + <input type="text" name="max_file_size" value="<?php echo $config['max_file_size']; ?>"> in bytes. + <em>(System Max. <?php echo Format::file_size(ini_get('upload_max_filesize')); ?>)</em> + <font class="error"> <?php echo $errors['max_file_size']; ?></font> + </td> + </tr> + <tr> + <td width="180">Ticket Response Files:</td> + <td> + <input type="checkbox" name="email_attachments" <?php echo $config['email_attachments']?'checked="checked"':''; ?> >Email attachments to the user + </td> + </tr> + <tr> + <th colspan="2"> + <em><strong>Accepted File Types</strong>: Limit the type of files users are allowed to submit. + <font class="error"> <?php echo $errors['allowed_filetypes']; ?></font></em> + </th> + </tr> + <tr> + <td colspan="2"> + <em>Enter allowed file extensions separated by a comma. e.g .doc, .pdf. To accept all files enter wildcard <b><i>.*</i></b> i.e dotStar (NOT Recommended).</em><br> + <textarea name="allowed_filetypes" cols="21" rows="4" style="width: 65%;" wrap="hard" ><?php echo $config['allowed_filetypes']; ?></textarea> + </td> + </tr> </tbody> </table> <p style="padding-left:250px;"> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index a58206963acf4deb36fd48bf17d80c2ea63020aa..ddd2839b24a720341841edd2c9b81e4efd218164 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -27,7 +27,7 @@ if($ticket->isAssigned() && ( $warn.=' <span class="Icon assignedTicket">Ticket is assigned to '.implode('/', $ticket->getAssignees()).'</span>'; if(!$errors['err'] && ($lock && $lock->getStaffId()!=$thisstaff->getId())) $errors['err']='This ticket is currently locked by '.$lock->getStaffName(); -if(!$errors['err'] && ($emailBanned=EmailFilter::isBanned($ticket->getEmail()))) +if(!$errors['err'] && ($emailBanned=TicketFilter::isBanned($ticket->getEmail()))) $errors['err']='Email is in banlist! Must be removed before any reply/response'; $unbannable=($emailBanned) ? BanList::includes($ticket->getEmail()) : false; diff --git a/include/upgrader/sql/15b30765-dd0022fb.cleanup.sql b/include/upgrader/sql/15b30765-dd0022fb.cleanup.sql new file mode 100644 index 0000000000000000000000000000000000000000..fdbd27d1f799d5da54b12d60522aa451f177798c --- /dev/null +++ b/include/upgrader/sql/15b30765-dd0022fb.cleanup.sql @@ -0,0 +1,10 @@ + +-- Drop fields we no longer need in the reference table. +-- NOTE: This was moved from the 1.6* major upgrade script because the +-- handling of attachments changed with dd0022fb +ALTER TABLE `%TABLE_PREFIX%ticket_attachment` + DROP `file_size`, + DROP `file_name`, + DROP `file_key`, + DROP `updated`, + DROP `deleted`; diff --git a/include/upgrader/sql/15b30765-dd0022fb.patch.sql b/include/upgrader/sql/15b30765-dd0022fb.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..0006139d679fdf9af8924b639b488e3edc0f2c34 --- /dev/null +++ b/include/upgrader/sql/15b30765-dd0022fb.patch.sql @@ -0,0 +1,26 @@ +/** + * @version v1.7 RC2+ + * @signature dd0022fb14892c0bb6a9700392df2de7 + * + * Migrate file attachment data from %file to %file_chunk + * + */ + +DROP TABLE IF EXISTS `%TABLE_PREFIX%file_chunk`; +CREATE TABLE `%TABLE_PREFIX%file_chunk` ( + `file_id` int(11) NOT NULL, + `chunk_id` int(11) NOT NULL, + `filedata` longblob NOT NULL, + PRIMARY KEY (`file_id`, `chunk_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `%TABLE_PREFIX%file_chunk` (`file_id`, `chunk_id`, `filedata`) + SELECT `id`, 0, `filedata` + FROM `%TABLE_PREFIX%file`; + +ALTER TABLE `%TABLE_PREFIX%file` DROP COLUMN `filedata`; +OPTIMIZE TABLE `%TABLE_PREFIX%file`; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='dd0022fb14892c0bb6a9700392df2de7'; diff --git a/include/upgrader/sql/1da1bcba-15b30765.patch.sql b/include/upgrader/sql/1da1bcba-15b30765.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..cb37a02f4af2e3c86f0367674454b22554973c3b --- /dev/null +++ b/include/upgrader/sql/1da1bcba-15b30765.patch.sql @@ -0,0 +1,11 @@ +/** + * @version v1.7 RC2+ + * @signature 15b3076533123ff617801d89861136c8 + * + * Transitional patch. + * + */ + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='15b3076533123ff617801d89861136c8'; diff --git a/include/upgrader/sql/c00511c7-7be60a84.cleanup.sql b/include/upgrader/sql/c00511c7-7be60a84.cleanup.sql index 01d69e2d72b732b2d7c3f618fd6e595ccb7c67f7..eabc72c9e22d724978f4f5449901ea5157b2b6a1 100644 --- a/include/upgrader/sql/c00511c7-7be60a84.cleanup.sql +++ b/include/upgrader/sql/c00511c7-7be60a84.cleanup.sql @@ -4,14 +4,6 @@ ALTER TABLE `%TABLE_PREFIX%config` DROP COLUMN `timezone_offset`, DROP COLUMN `api_passphrase`; --- Drop fields we no longer need in the reference table. -ALTER TABLE `%TABLE_PREFIX%ticket_attachment` - DROP `file_size`, - DROP `file_name`, - DROP `file_key`, - DROP `updated`, - DROP `isdeleted`; - -- Drop fields we no longer need in staff table. ALTER TABLE `%TABLE_PREFIX%staff` DROP `append_signature`, diff --git a/include/upgrader/sql/c00511c7-7be60a84.patch.sql b/include/upgrader/sql/c00511c7-7be60a84.patch.sql index 638248b976019a670c773578b564d99a3daacc6a..fdefd09bea960de619fc5a1feac8bd69d3c77b8e 100644 --- a/include/upgrader/sql/c00511c7-7be60a84.patch.sql +++ b/include/upgrader/sql/c00511c7-7be60a84.patch.sql @@ -63,7 +63,7 @@ ALTER TABLE `%TABLE_PREFIX%config` ADD `transfer_alert_dept_manager` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '1' AFTER `transfer_alert_assigned` , ADD `transfer_alert_dept_members` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `transfer_alert_dept_manager`, ADD `send_sys_errors` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1' AFTER `enable_email_piping`, - ADD `enable_kb` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1' AFTER `use_email_priority`, + ADD `enable_kb` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `use_email_priority`, ADD `enable_premade` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1' AFTER `enable_kb`, ADD `show_related_tickets` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1' AFTER `auto_assign_reopened_tickets`, ADD `schema_signature` CHAR( 32 ) NOT NULL AFTER `ostversion`; diff --git a/include/upgrader/sql/d0e37dca-1da1bcba.patch.sql b/include/upgrader/sql/d0e37dca-1da1bcba.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..3d3bda68e2fd49293e1a73545506416c7a1cc126 --- /dev/null +++ b/include/upgrader/sql/d0e37dca-1da1bcba.patch.sql @@ -0,0 +1,23 @@ +/** + * @version v1.7 RC3 + * @signature 1da1bcbafcedc65efef58f142a48ac91 + * + * Upgrade from 1.6 RC3 + filters + * + */ + +RENAME TABLE `%TABLE_PREFIX%email_filter` TO `%TABLE_PREFIX%filter`; + +RENAME TABLE `%TABLE_PREFIX%email_filter_rule` TO `%TABLE_PREFIX%filter_rule`; + +ALTER TABLE `%TABLE_PREFIX%filter` CHANGE `reject_email` `reject_ticket` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; + +ALTER TABLE `%TABLE_PREFIX%filter` + ADD `target` ENUM( 'Any', 'Web', 'Email', 'API' ) NOT NULL DEFAULT 'Any' AFTER `sla_id` , + ADD INDEX ( `target` ); + +UPDATE `%TABLE_PREFIX%filter` SET `target` = 'Email' WHERE `email_id` != 0; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='1da1bcbafcedc65efef58f142a48ac91'; diff --git a/main.inc.php b/main.inc.php index abcbfeabdaee005bdd83abb7719ac163e0e531af..235a6341a2db15e7be7b5c1e313e6b85e9b678f3 100644 --- a/main.inc.php +++ b/main.inc.php @@ -62,9 +62,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-RC2'); //Shown on admin panel - define('SCHEMA_SIGNATURE','d0e37dca324648f1ce2d10528a6026d4'); //MD5 signature of the db schema. (used to trigger upgrades) - + define('THIS_VERSION','1.7-RC2+'); //Shown on admin panel + define('SCHEMA_SIGNATURE','dd0022fb14892c0bb6a9700392df2de7'); //MD5 signature of the db schema. (used to trigger upgrades) #load config info $configfile=''; if(file_exists(ROOT_DIR.'ostconfig.php')) //Old installs prior to v 1.6 RC5 @@ -132,6 +131,7 @@ define('SYSLOG_TABLE',TABLE_PREFIX.'syslog'); define('SESSION_TABLE',TABLE_PREFIX.'session'); define('FILE_TABLE',TABLE_PREFIX.'file'); + define('FILE_CHUNK_TABLE',TABLE_PREFIX.'file_chunk'); define('STAFF_TABLE',TABLE_PREFIX.'staff'); define('DEPT_TABLE',TABLE_PREFIX.'department'); @@ -159,8 +159,10 @@ define('EMAIL_TABLE',TABLE_PREFIX.'email'); define('EMAIL_TEMPLATE_TABLE',TABLE_PREFIX.'email_template'); - define('EMAIL_FILTER_TABLE',TABLE_PREFIX.'email_filter'); - define('EMAIL_FILTER_RULE_TABLE',TABLE_PREFIX.'email_filter_rule'); + + define('FILTER_TABLE',TABLE_PREFIX.'filter'); + define('FILTER_RULE_TABLE',TABLE_PREFIX.'filter_rule'); + define('BANLIST_TABLE',TABLE_PREFIX.'email_banlist'); //Not in use anymore....as of v 1.7 define('SLA_TABLE',TABLE_PREFIX.'sla'); diff --git a/scp/apikeys.php b/scp/apikeys.php index e393a31c5f2bea0647a238240ba5e9021ad3c9ab..1e17675442066356a077041f03357fa0c3913414 100644 --- a/scp/apikeys.php +++ b/scp/apikeys.php @@ -96,7 +96,7 @@ $page='apikeys.inc.php'; if($api || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) $page='apikey.inc.php'; -$nav->setTabActive('settings'); +$nav->setTabActive('manage'); require(STAFFINC_DIR.'header.inc.php'); require(STAFFINC_DIR.$page); include(STAFFINC_DIR.'footer.inc.php'); diff --git a/scp/banlist.php b/scp/banlist.php index 081fde9b4ed0cd18713cab78e795c2e0eaf1df1b..a31d91d7e8d2392c2d7e23dba570a07257532ec8 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='. + $sql='UPDATE '.FILTER_RULE_TABLE.' SET isactive=1 WHERE filter_id='. db_input($filter->getId()). ' AND id IN ('. implode(',', db_input($_POST['ids'])).')'; @@ -81,7 +81,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='. + $sql='UPDATE '.FILTER_RULE_TABLE.' SET isactive=0 WHERE filter_id='. db_input($filter->getId()). ' AND id IN ('. implode(',', db_input($_POST['ids'])).')'; diff --git a/scp/css/scp.css b/scp/css/scp.css index 0042ed4306a406337f3a3deec2811467c06a325d..fd4faed6dc77cb4e1ebee9ca0c1a8d71b661dab6 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -247,6 +247,13 @@ 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.ticket-settings { background:url(../images/icons/ticket-settings.gif) } +a.email-settings { background:url(../images/icons/email-settings.gif) } +a.kb-settings { background:url(../images/icons/kb-settings.gif) } +a.alert-settings { background:url(../images/icons/alert-settings.gif) } +a.email-autoresponders { background:url(../images/icons/email-autoresponders.gif) } + + a.sla { background:url(../images/icons/slas.png) } a.newsla { background:url(../images/icons/new_sla.png) } @@ -259,7 +266,8 @@ 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.ticketFilters { background:url(../images/icons/ticket_filters.png) } +a.newTicketFilter { background:url(../images/icons/new_ticket_filter.png) } a.emailSettings { background:url(../images/icons/emails.png) } a.emailDiagnostic { background:url(../images/icons/email_diagnostic.gif) } @@ -1292,7 +1300,7 @@ time { #advanced-search input[type="reset"], #advanced-search input[type="button"], #print-options input[type="reset"], -#print-options input[type="button"] { +#print-options input[type="button"] { opacity:0.7; } diff --git a/scp/departments.php b/scp/departments.php index ef1acb12d62adcaa3c6285a210e7aa9d3f57fc29..31fc4c6df5602f5959d9f4585517a8ebf04fc008 100644 --- a/scp/departments.php +++ b/scp/departments.php @@ -104,7 +104,7 @@ $page='departments.inc.php'; if($dept || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) $page='department.inc.php'; -$nav->setTabActive('depts'); +$nav->setTabActive('staff'); require(STAFFINC_DIR.'header.inc.php'); require(STAFFINC_DIR.$page); include(STAFFINC_DIR.'footer.inc.php'); diff --git a/scp/filters.php b/scp/filters.php index 4ce0f30765f5192617f22345c00571f26d206af2..29045410fe685f10a86a362942f34259a6401edd 100644 --- a/scp/filters.php +++ b/scp/filters.php @@ -49,7 +49,7 @@ if($_POST){ }else{ $count=count($_POST['ids']); if($_POST['enable']){ - $sql='UPDATE '.EMAIL_FILTER_TABLE.' SET isactive=1 WHERE id IN ('. + $sql='UPDATE '.FILTER_TABLE.' SET isactive=1 WHERE id IN ('. implode(',', db_input($_POST['ids'])).')'; if(db_query($sql) && ($num=db_affected_rows())){ if($num==$count) @@ -60,7 +60,7 @@ if($_POST){ $errors['err']='Unable to enable selected filters'; } }elseif($_POST['disable']){ - $sql='UPDATE '.EMAIL_FILTER_TABLE.' SET isactive=0 WHERE id IN ('. + $sql='UPDATE '.FILTER_TABLE.' SET isactive=0 WHERE id IN ('. implode(',', db_input($_POST['ids'])).')'; if(db_query($sql) && ($num=db_affected_rows())) { if($num==$count) @@ -100,7 +100,7 @@ $page='filters.inc.php'; if($filter || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) $page='filter.inc.php'; -$nav->setTabActive('emails'); +$nav->setTabActive('manage'); require(STAFFINC_DIR.'header.inc.php'); require(STAFFINC_DIR.$page); include(STAFFINC_DIR.'footer.inc.php'); diff --git a/scp/helptopics.php b/scp/helptopics.php index 5bd1ded7048f26307f2cd18d09d6b3a946f13f65..37e865b22057483bc87595023ad92ce9be1bb306 100644 --- a/scp/helptopics.php +++ b/scp/helptopics.php @@ -96,7 +96,7 @@ $page='helptopics.inc.php'; if($topic || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) $page='helptopic.inc.php'; -$nav->setTabActive('topics'); +$nav->setTabActive('manage'); require(STAFFINC_DIR.'header.inc.php'); require(STAFFINC_DIR.$page); include(STAFFINC_DIR.'footer.inc.php'); diff --git a/scp/images/icons/alert-settings.gif b/scp/images/icons/alert-settings.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c8a0b1debd05f87bb53b0d43fb10f153738df1d Binary files /dev/null and b/scp/images/icons/alert-settings.gif differ diff --git a/scp/images/icons/email-autoresponders.gif b/scp/images/icons/email-autoresponders.gif new file mode 100644 index 0000000000000000000000000000000000000000..5c118b46b3daf67261a92bd252a97ae4a34b4153 Binary files /dev/null and b/scp/images/icons/email-autoresponders.gif differ diff --git a/scp/images/icons/email-settings.gif b/scp/images/icons/email-settings.gif new file mode 100644 index 0000000000000000000000000000000000000000..6dc8aa0023e6d69355200e53ac492d61346f83d3 Binary files /dev/null and b/scp/images/icons/email-settings.gif differ diff --git a/scp/images/icons/kb-settings.gif b/scp/images/icons/kb-settings.gif new file mode 100644 index 0000000000000000000000000000000000000000..991f3c812f0216eb0957d619a92da2e55d22131c Binary files /dev/null and b/scp/images/icons/kb-settings.gif differ diff --git a/scp/images/icons/new_ticket_filter.png b/scp/images/icons/new_ticket_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..00c091ebff2b5c2f20729f4f0b16a6405022b5ea Binary files /dev/null and b/scp/images/icons/new_ticket_filter.png differ diff --git a/scp/images/icons/ticket-settings.gif b/scp/images/icons/ticket-settings.gif new file mode 100644 index 0000000000000000000000000000000000000000..f5d5234a95f4b5d26e7a8eed41054a9377077d90 Binary files /dev/null and b/scp/images/icons/ticket-settings.gif differ diff --git a/scp/images/icons/ticket_filters.png b/scp/images/icons/ticket_filters.png new file mode 100644 index 0000000000000000000000000000000000000000..4f2065212f29d70d1f17f316de5f6b8a77b3f950 Binary files /dev/null and b/scp/images/icons/ticket_filters.png differ diff --git a/scp/images/icons/ticket_source_other.png b/scp/images/icons/ticket_source_other.png new file mode 100644 index 0000000000000000000000000000000000000000..f7a78f27412669beb75c2371d035d5aa104bdce4 Binary files /dev/null and b/scp/images/icons/ticket_source_other.png differ diff --git a/scp/settings.php b/scp/settings.php index 9058fe42ca52c4f9b428bbc52f2ed2cb61f80a8a..0a3f97a912d8fdf6d92dc42ddf035f36bc61b12f 100644 --- a/scp/settings.php +++ b/scp/settings.php @@ -15,49 +15,28 @@ **********************************************************************/ require('admin.inc.php'); $errors=array(); -$SettingOptions=array('general'=>'General Settings', - 'dates'=>'Date and Time Options', - 'tickets'=>'Ticket Settings and Options', - 'emails'=>'Email Settings', - 'attachments'=>'Attachments Settings', - 'kb'=>'Knowledgebase Settings', - 'autoresponders'=>'Autoresponder Settings', - 'alerts'=>'Alerts and Notices Settings'); - +$settingOptions=array( + 'system' => 'System Settings', + 'tickets' => 'Ticket Settings and Options', + 'emails' => 'Email Settings', + 'kb' => 'Knowledgebase Settings', + 'autoresp' => 'Autoresponder Settings', + 'alerts' => 'Alerts and Notices Settings'); //Handle a POST. -if($_POST && !$errors){ - $errors=array(); - if($cfg && $cfg->updateSettings($_POST,$errors)){ - $msg=Format::htmlchars($SettingOptions[$_POST['t']]).' Updated Successfully'; +if($_POST && !$errors) { + if($cfg && $cfg->updateSettings($_POST,$errors)) { + $msg=Format::htmlchars($settingOptions[$_POST['t']]).' Updated Successfully'; $cfg->reload(); - }elseif(!$errors['err']){ - $errors['err']='Unable to update system settings - correct any errors below and try again'; + } elseif(!$errors['err']) { + $errors['err']='Unable to update settings - correct errors below and try again'; } } -$target=($_REQUEST['t'] && $SettingOptions[$_REQUEST['t']])?$_REQUEST['t']:'general'; - -$nav->setTabActive('settings'); -require(STAFFINC_DIR.'header.inc.php'); -?> -<h2>System Preferences and Settings - <span>osTicket (v<?php echo $cfg->getVersion(); ?>)</span></h2> -<div style="padding-top:10px;padding-bottom:5px;"> - <form method="get" action="settings.php"> - Setting Option: - <select id="setting_options" name="t" style="width:300px;"> - <option value="">— Select Setting Group —</option> - <?php - foreach($SettingOptions as $k=>$v) { - $sel=($target==$k)?'selected="selected"':''; - echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v); - } - ?> - </select> - <input type="submit" value="Go"> - </form> -</div> -<?php +$target=($_REQUEST['t'] && $settingOptions[$_REQUEST['t']])?$_REQUEST['t']:'system'; $config=($errors && $_POST)?Format::input($_POST):Format::htmlchars($cfg->getConfigInfo()); + +$nav->setTabActive('settings', ('settings.php?t='.$target)); +require_once(STAFFINC_DIR.'header.inc.php'); include_once(STAFFINC_DIR."settings-$target.inc.php"); include_once(STAFFINC_DIR.'footer.inc.php'); ?> diff --git a/scp/slas.php b/scp/slas.php index 8f3b0f75ca331381beb6a348f46c2a703853b229..8dd5b86c90b33b6cf2e88558f9ac679f63af77be 100644 --- a/scp/slas.php +++ b/scp/slas.php @@ -96,7 +96,7 @@ $page='slaplans.inc.php'; if($sla || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) $page='slaplan.inc.php'; -$nav->setTabActive('settings'); +$nav->setTabActive('manage'); require(STAFFINC_DIR.'header.inc.php'); require(STAFFINC_DIR.$page); include(STAFFINC_DIR.'footer.inc.php'); diff --git a/scp/tickets.php b/scp/tickets.php index 8c799a2a6a02a26c4185afece329ffc5e57cdcf4..77eec14eab39caed7e1c235ffa079bdeaa658ace 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -52,7 +52,7 @@ if($_POST && !$errors): $errors['err']='Action Denied. Ticket is locked by someone else!'; //Make sure the email is not banned - if(!$errors['err'] && EmailFilter::isBanned($ticket->getEmail())) + if(!$errors['err'] && TicketFilter::isBanned($ticket->getEmail())) $errors['err']='Email is in banlist. Must be removed to reply.'; $wasOpen =($ticket->isOpen()); diff --git a/setup/inc/sql/osTicket-mysql.sql b/setup/inc/sql/osTicket-mysql.sql index 5cfba00d6538506e29e136f188610df4508551cb..53e3c551999567641bd33c71b7056f45de546c6c 100644 --- a/setup/inc/sql/osTicket-mysql.sql +++ b/setup/inc/sql/osTicket-mysql.sql @@ -91,7 +91,7 @@ CREATE TABLE `%TABLE_PREFIX%config` ( `clickable_urls` tinyint(1) unsigned NOT NULL default '1', `allow_priority_change` tinyint(1) unsigned NOT NULL default '0', `use_email_priority` tinyint(1) unsigned NOT NULL default '0', - `enable_kb` tinyint(1) unsigned NOT NULL default '1', + `enable_kb` tinyint(1) unsigned NOT NULL default '0', `enable_premade` tinyint(1) unsigned NOT NULL default '1', `enable_captcha` tinyint(1) unsigned NOT NULL default '0', `enable_auto_cron` tinyint(1) unsigned NOT NULL default '0', @@ -228,14 +228,14 @@ CREATE TABLE `%TABLE_PREFIX%email` ( KEY `dept_id` (`dept_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -DROP TABLE IF EXISTS `%TABLE_PREFIX%email_filter`; -CREATE TABLE `%TABLE_PREFIX%email_filter` ( +DROP TABLE IF EXISTS `%TABLE_PREFIX%filter`; +CREATE TABLE `%TABLE_PREFIX%filter` ( `id` int(11) unsigned NOT NULL auto_increment, `execorder` int(10) unsigned NOT NULL default '99', `isactive` tinyint(1) unsigned NOT NULL default '1', `match_all_rules` tinyint(1) unsigned NOT NULL default '0', `stop_onmatch` tinyint(1) unsigned NOT NULL default '0', - `reject_email` tinyint(1) unsigned NOT NULL default '0', + `reject_ticket` tinyint(1) unsigned NOT NULL default '0', `use_replyto_email` tinyint(1) unsigned NOT NULL default '0', `disable_autoresponder` tinyint(1) unsigned NOT NULL default '0', `canned_response_id` int(11) unsigned NOT NULL default '0', @@ -245,21 +245,23 @@ CREATE TABLE `%TABLE_PREFIX%email_filter` ( `staff_id` int(10) unsigned NOT NULL default '0', `team_id` int(10) unsigned NOT NULL default '0', `sla_id` int(10) unsigned NOT NULL default '0', + `target` ENUM( 'Any', 'Web', 'Email', 'API' ) NOT NULL DEFAULT 'Any', `name` varchar(32) NOT NULL default '', `notes` text, `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), + KEY `target` (`target`), KEY `email_id` (`email_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -INSERT INTO `%TABLE_PREFIX%email_filter` ( - `id`,`isactive`,`execorder`,`reject_email`,`name`,`notes`,`created`) +INSERT INTO `%TABLE_PREFIX%filter` ( + `id`,`isactive`,`execorder`,`reject_ticket`,`name`,`notes`,`created`) VALUES (1, 1, 99, 1, 'SYSTEM BAN LIST', 'Internal list for email banning. Do not remove', NOW()); -DROP TABLE IF EXISTS `%TABLE_PREFIX%email_filter_rule`; -CREATE TABLE `%TABLE_PREFIX%email_filter_rule` ( +DROP TABLE IF EXISTS `%TABLE_PREFIX%filter_rule`; +CREATE TABLE `%TABLE_PREFIX%filter_rule` ( `id` int(11) unsigned NOT NULL auto_increment, `filter_id` int(10) unsigned NOT NULL default '0', `what` enum('name','email','subject','body','header') NOT NULL, @@ -274,7 +276,7 @@ CREATE TABLE `%TABLE_PREFIX%email_filter_rule` ( UNIQUE `filter` (`filter_id`, `what`, `how`, `val`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -INSERT INTO `%TABLE_PREFIX%email_filter_rule` ( +INSERT INTO `%TABLE_PREFIX%filter_rule` ( `id`, `filter_id`, `isactive`, `what`,`how`,`val`,`created`) VALUES (1, 1, 1, 'email', 'equal', 'test@example.com',NOW()); @@ -325,15 +327,24 @@ CREATE TABLE `%TABLE_PREFIX%file` ( `size` varchar(25) NOT NULL default '', `hash` varchar(125) NOT NULL, `name` varchar(255) NOT NULL default '', - `filedata` longblob NOT NULL, `created` datetime NOT NULL, PRIMARY KEY (`id`), KEY `hash` (`hash`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -INSERT INTO `%TABLE_PREFIX%file` (`id`, `type`, `size`, `hash`, `name`, `filedata`, `created`) VALUES -(1, 'text/plain', '25', '670c6cc1d1dfc97fad20e5470251b255', 'osTicket.txt', 0x43616e6e6564206174746163686d656e747320726f636b210a, NOW()); +INSERT INTO `%TABLE_PREFIX%file` (`id`, `type`, `size`, `hash`, `name`, `created`) VALUES +(1, 'text/plain', '25', '670c6cc1d1dfc97fad20e5470251b255', 'osTicket.txt', NOW()); +DROP TABLE IF EXISTS `%TABLE_PREFIX%file_chunk`; +CREATE TABLE `%TABLE_PREFIX%file_chunk` ( + `file_id` int(11) NOT NULL, + `chunk_id` int(11) NOT NULL, + `filedata` longblob NOT NULL, + PRIMARY KEY (`file_id`, `chunk_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `%TABLE_PREFIX%file_chunk` (`file_id`, `chunk_id`, `filedata`) +VALUES (1, 0, 0x43616e6e6564206174746163686d656e747320726f636b210a); DROP TABLE IF EXISTS `%TABLE_PREFIX%groups`; CREATE TABLE `%TABLE_PREFIX%groups` ( diff --git a/setup/inc/sql/osTicket-mysql.sql.md5 b/setup/inc/sql/osTicket-mysql.sql.md5 index 63d2ce443b0448ca776ebc37b298a3c4ff93c387..e96b33654d6d528ac92f4613636491d6ffee3e0a 100644 --- a/setup/inc/sql/osTicket-mysql.sql.md5 +++ b/setup/inc/sql/osTicket-mysql.sql.md5 @@ -1 +1 @@ -d0e37dca324648f1ce2d10528a6026d4 +dd0022fb14892c0bb6a9700392df2de7 diff --git a/setup/setup.inc.php b/setup/setup.inc.php index 07b86c6ffa43e13338f3b1f6c176e0cee2f53316..f2d0bb5e1095ca82d1a148b11b25f73e9193edf1 100644 --- a/setup/setup.inc.php +++ b/setup/setup.inc.php @@ -15,10 +15,16 @@ **********************************************************************/ #This version - changed on every release -define('THIS_VERSION', '1.7-RC2'); +define('THIS_VERSION', '1.7-RC2+'); -#inits -error_reporting(E_ALL ^ E_NOTICE); //turn on errors?? +#inits - error reporting. +$error_reporting = E_ALL & ~E_NOTICE; +if (defined('E_STRICT')) # 5.4.0 + $error_reporting &= ~E_STRICT; +if (defined('E_DEPRECATED')) # 5.3.0 + $error_reporting &= ~(E_DEPRECATED | E_USER_DEPRECATED); + +error_reporting($error_reporting); ini_set('magic_quotes_gpc', 0); ini_set('session.use_trans_sid', 0); ini_set('session.cache_limiter', 'nocache');