diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css index 73a76e4aa4e4089bda6264ea9d4fd07ab65448cc..8adafc32edbd4b7335de5e75769d25a46a86bfda 100644 --- a/assets/default/css/theme.css +++ b/assets/default/css/theme.css @@ -6,7 +6,7 @@ html { } body { margin: 0; - font-size: 13px; + font-size: 14px; line-height: 1.231; padding: 0; } @@ -14,7 +14,7 @@ body, input, select, textarea { - font-family: sans-serif; + font-family: "Helvetica Neue", sans-serif; color: #000; } b, @@ -201,7 +201,7 @@ h2, .subject { border: 1px solid #0a0; background: url('../images/icons/ok.png') 10px 50% no-repeat #e0ffe0; } -#msg_warning { +#msg_warning, .warning-banner { margin: 0; padding: 5px 10px 5px 36px; height: 16px; @@ -413,8 +413,8 @@ body { } .front-page-button { } -#landing_page .welcome { - width: 560px; +.main-content { + width: 565px; } #landing_page #new_ticket { margin-top: 40px; @@ -528,10 +528,10 @@ body { overflow: hidden; } .article-title { - font-weight: 400; + font-weight: 500; } .faq-content .article-title { - font-size: 16pt; + font-size: 17pt; margin-top: 15px; } #kb-search { @@ -727,12 +727,14 @@ label.required, span.required { } #reply { margin-top: 20px; - padding: 10px 5px; + padding: 10px; background: #f9f9f9; border: 1px solid #ccc; } #reply h2 { margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 2px dotted rgba(0,0,0,0.1); } #reply > table { width: auto; @@ -851,6 +853,7 @@ a.refresh { } .infoTable th { text-align: left; + padding: 3px 8px; } #ticketThread table.response, #ticketThread table.message { @@ -885,7 +888,6 @@ a.refresh { #ticketThread .info a { display: inline-block; margin: 5px 10px 5px 0; - padding-left: 24px; height: 16px; line-height: 16px; background-position: 0 50%; @@ -903,7 +905,6 @@ a.refresh { border: 1px solid #aaa; cursor: pointer; font-size: 11px; - height: 18px; overflow: hidden; background-color: #dddddd; background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #efefef), color-stop(100% #dddddd)); @@ -915,7 +916,6 @@ a.refresh { padding: 0 5px; text-decoration: none; line-height:18px; - float:right; margin-left:5px; } .action-button span, @@ -985,9 +985,7 @@ img.sign-in-image { .sidebar { margin-bottom: 20px; margin-left: 20px; -} -#landing_page .sidebar { - width: 220px; + width: 215px; } .rtl .sidebar { margin-left: 0; @@ -996,15 +994,19 @@ img.sign-in-image { .sidebar .content { padding: 10px; border: 1px solid #C8DDFA; background: #F7FBFE; } +.sidebar .content:empty { + display: none; +} .sidebar section .header { font-weight: bold; + margin-bottom: 0.3em; } .sidebar section + section { margin-top: 15px; } .search-form { - padding: 12px 0px 20px 0; + padding-top: 12px; } .searchbar .search, .search-form .search { @@ -1029,13 +1031,13 @@ img.sign-in-image { } .span4 { display: inline-block; - width: 30.5%; + width: 29.5%; margin: 0 1%; vertical-align: top; } .span8 { display: inline-block; - width: 64.5%; + width: 66.5%; margin: 0 1%; vertical-align: top; } @@ -1047,3 +1049,33 @@ img.sign-in-image { overflow: hidden; text-overflow: ellipsis; } +.image-hover a.action-button:hover, +.image-hover a.action-button { + color: initial !important; + text-decoration: none; +} +table.custom-data { + margin-top: 10px; +} +table.custom-data th { + width: 25%; +} +table.custom-data th { + background-color: #F4FAFF; + padding: 3px 8px; +} +table .headline, +table.custom-data .headline { + border-bottom: 2px solid #ddd; + border-bottom: 2px solid rgba(0,0,0,0.15); + font-weight: bold; + background-color: white; +} +#ticketInfo h1 { + padding-bottom: 10px; + margin-bottom: 5px; + border-bottom: 2px dotted rgba(0, 0, 0, 0.15); +} +#ticketInfo h1 small { + font-weight: normal; +} diff --git a/css/chosen-sprite.png b/css/chosen-sprite.png deleted file mode 100644 index c57da70b4b5b1e08a6977ddde182677af0e5e1b8..0000000000000000000000000000000000000000 Binary files a/css/chosen-sprite.png and /dev/null differ diff --git a/css/chosen-sprite@2x.png b/css/chosen-sprite@2x.png deleted file mode 100644 index 6b50545202cb4770039362c55025b0b9824663ad..0000000000000000000000000000000000000000 Binary files a/css/chosen-sprite@2x.png and /dev/null differ diff --git a/css/chosen.min.css b/css/chosen.min.css deleted file mode 100644 index 8bfa3c8b0bc07a7448e0659ebcc4f655c26e7a1f..0000000000000000000000000000000000000000 --- a/css/chosen.min.css +++ /dev/null @@ -1,3 +0,0 @@ -/* Chosen v1.2.0 | (c) 2011-2014 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ - -.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;zoom:1;*display:inline;-webkit-user-select:none;-moz-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;left:-9999px;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;box-shadow:0 4px 5px rgba(0,0,0,.15)}.chosen-container.chosen-with-drop .chosen-drop{left:0}.chosen-container a{cursor:pointer}.chosen-container a:hover{text-decoration:none} .chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),color-stop(100%,#f4f4f4));background:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:#fff url(chosen-sprite.png) no-repeat 100% -20px;background:url(chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;left:-9999px}.chosen-container .chosen-results{color:#444;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:-webkit-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-moz-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-o-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#777;display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto!important;height:1%;border:1px solid #aaa;background-color:#fff;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background-image:-webkit-linear-gradient(#eee 1%,#fff 15%);background-image:-moz-linear-gradient(#eee 1%,#fff 15%);background-image:-o-linear-gradient(#eee 1%,#fff 15%);background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:transparent!important;box-shadow:none;color:#999;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #aaa;max-width:100%;border-radius:3px;background-color:#eee;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;-moz-border-radius-bottomright:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#eee),color-stop(80%,#fff));background-image:-webkit-linear-gradient(#eee 20%,#fff 80%);background-image:-moz-linear-gradient(#eee 20%,#fff 80%);background-image:-o-linear-gradient(#eee 20%,#fff 80%);background-image:linear-gradient(#eee 20%,#fff 80%);box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:0;background:transparent}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#222!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single-nosearch .chosen-search,.chosen-rtl .chosen-drop{left:9999px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:0}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:#fff url(chosen-sprite.png) no-repeat -30px -20px;background:url(chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-resolution:144dpi){.chosen-rtl .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-container-single .chosen-search input[type=text],.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}} diff --git a/css/redactor.css b/css/redactor.css index 46c298067f23b37c16b14b0a3163dff317062cd1..a3dc978446c5ec6be163994270f9a5698f438495 100644 --- a/css/redactor.css +++ b/css/redactor.css @@ -17,7 +17,8 @@ .redactor-box { position: relative; overflow: visible; - background: #fff; + overflow-x: hidden; + background: #ddd; } .redactor-box textarea { display: block; @@ -41,8 +42,7 @@ /* Z-index setup */ -.redactor-editor, -.redactor-box { +.redactor-editor { background: #fff; } .redactor-editor, @@ -109,6 +109,7 @@ body .redactor-box-fullscreen { font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif; font-size: 14px; line-height: 1.6em; + box-shadow: 5px 0 10px -8px rgba(0,0,0,0.5); } .redactor-editor:focus { outline: none; @@ -124,16 +125,16 @@ body .redactor-box-fullscreen { } .redactor-placeholder:after { position: absolute; - top: 20px; - left: 20px; + top: 10px; + left: 10px; content: attr(placeholder); color: #999 !important; font-weight: normal !important; } /* Placeholder in linebreaks mode */ .redactor-linebreaks.redactor-placeholder:after { - top: 20px; - left: 20px; + top: 10px; + left: 10px; } /* Toolbar @@ -148,8 +149,9 @@ body .redactor-box-fullscreen { font-size: 14px !important; line-height: 1 !important; background: #fff; - border: none; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + border: 1px solid rgba(0,0,0,0.05); + border-bottom: 0; + box-shadow: 0 1px 4px -2px rgba(0, 0, 0, 0.4); } .redactor-toolbar:after { content: ""; diff --git a/css/select2.min.css b/css/select2.min.css new file mode 100644 index 0000000000000000000000000000000000000000..1c7234426a473d3e017340ecda88f880dc1e3476 --- /dev/null +++ b/css/select2.min.css @@ -0,0 +1 @@ +.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle;}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px;}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap;}.select2-container .select2-search--inline{float:left;}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051;}.select2-results{display:block;}.select2-results__options{list-style:none;margin:0;padding:0;}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none;}.select2-results__option[aria-selected]{cursor:pointer;}.select2-container--open .select2-dropdown{left:0;}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-search--dropdown{display:block;padding:4px;}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box;}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-search--dropdown.select2-search--hide{display:none;}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0);}.select2-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px;}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px;}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto;}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none;}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%;}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left;}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder{float:right;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0;}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none;}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0;}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa;}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--default .select2-results__option[role=group]{padding:0;}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999;}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd;}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em;}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white;}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic .select2-selection--single{background-color:#f6f6f6;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:-o-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:linear-gradient(to bottom, #ffffff 50%, #eeeeee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px;}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#cccccc', GradientType=0);}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto;}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:-o-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:linear-gradient(to bottom, #ffffff 0%, #eeeeee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #ffffff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0;}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none;}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0;}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;}.select2-container--classic .select2-dropdown{background-color:white;border:1px solid transparent;}.select2-container--classic .select2-dropdown--above{border-bottom:none;}.select2-container--classic .select2-dropdown--below{border-top:none;}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--classic .select2-results__option[role=group]{padding:0;}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey;}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:white;}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb;} \ No newline at end of file diff --git a/css/thread.css b/css/thread.css index 6fe012353f3137d0629f15d4ac73667694460c16..233f283158c5e8f98a67e1dad072bfac756e343d 100644 --- a/css/thread.css +++ b/css/thread.css @@ -208,15 +208,15 @@ } .thread-body h1, .thread-body .h1 { - font-size: 36px; + font-size: 30px; } .thread-body h2, .thread-body .h2 { - font-size: 30px; + font-size: 25px; } .thread-body h3, .thread-body .h3 { - font-size: 24px; + font-size: 21px; } .thread-body h4, .thread-body .h4 { diff --git a/file.php b/file.php index 22cc8094be840bc25b322a6d571921f697beb8f8..fda45974ed942b2e342a9a1efc8c0eaa06ce99c1 100644 --- a/file.php +++ b/file.php @@ -29,11 +29,16 @@ if (!$_GET['key'] // Validate session access hash - we want to make sure the link is FRESH! // and the user has access to the parent ticket!! if ($file->verifySignature($_GET['signature'], $_GET['expires'])) { - if (($s = @$_GET['s']) && strpos($file->getType(), 'image/') === 0) - return $file->display($s); + try { + if (($s = @$_GET['s']) && strpos($file->getType(), 'image/') === 0) + return $file->display($s); - // Download the file.. - $file->download(@$_GET['disposition'] ?: false, $_GET['expires']); + // Download the file.. + $file->download(@$_GET['disposition'] ?: false, $_GET['expires']); + } + catch (Exception $x) { + Http::response(500, 'Unable to find that file: '.$ex->getMessage()); + } } // else Http::response(404, __('Unknown or invalid file')); diff --git a/include/ajax.config.php b/include/ajax.config.php index c263bd4a253993240b27e4557060b1fadc689ae1..a4e11bf22ab1ea5bebbab361604edbf71e569065 100644 --- a/include/ajax.config.php +++ b/include/ajax.config.php @@ -46,7 +46,7 @@ class ConfigAjaxAPI extends AjaxController { 'has_rtl' => $rtl, 'lang_flag' => strtolower($info['flag'] ?: $locale ?: $sl), 'primary_lang_flag' => strtolower($primary_info['flag'] ?: $primary_locale ?: $primary_sl), - 'primary_language' => $primary, + 'primary_language' => Internationalization::rfc1766($primary), 'secondary_languages' => $cfg->getSecondaryLanguages(), 'page_size' => $thisstaff->getPageLimit(), ); @@ -70,7 +70,7 @@ class ConfigAjaxAPI extends AjaxController { 'lang' => $lang, 'short_lang' => $sl, 'has_rtl' => $rtl, - 'primary_language' => $cfg->getPrimaryLanguage(), + 'primary_language' => Internationalization::rfc1766($cfg->getPrimaryLanguage()), 'secondary_languages' => $cfg->getSecondaryLanguages(), ); diff --git a/include/ajax.draft.php b/include/ajax.draft.php index b01054ed8b9d6bf8c193e8420e4d54769c1cfce2..9fa61635d730f8e1a67a4ebe18ea35271bf7eee0 100644 --- a/include/ajax.draft.php +++ b/include/ajax.draft.php @@ -94,6 +94,10 @@ class DraftAjaxAPI extends AjaxController { )) ); + // Paste uploads in Chrome will have a name of 'blob' + if ($file[0]['name'] == 'blob') + $file[0]['name'] = 'screenshot-'.Misc::randCode(4); + if (isset($file[0]['tmp_name'])) { $ids = $draft->attachments->upload($file); } @@ -381,19 +385,20 @@ class DraftAjaxAPI extends AjaxController { function _findDraftBody($vars) { if (isset($vars['name'])) { $parts = array(); + // Support nested `name`, like trans[lang] if (preg_match('`(\w+)(?:\[(\w+)\])?(?:\[(\w+)\])?`', $_POST['name'], $parts)) { array_shift($parts); $focus = $vars; foreach ($parts as $p) $focus = $focus[$p]; - return urldecode($focus); + return $focus; } } $field_list = array('response', 'note', 'answer', 'body', 'message', 'issue', 'description'); foreach ($field_list as $field) { if (isset($vars[$field])) { - return urldecode($vars[$field]); + return $vars[$field]; } } diff --git a/include/ajax.reports.php b/include/ajax.reports.php index 6dbf60df52322a91d8b513fb530e2246eb9c0ede..6086e3cb0b65fb214c14dd141d874976c25c0916 100644 --- a/include/ajax.reports.php +++ b/include/ajax.reports.php @@ -19,6 +19,7 @@ if(!defined('INCLUDE_DIR')) die('403'); include_once(INCLUDE_DIR.'class.ticket.php'); +include_once(INCLUDE_DIR.'class.report.php'); /** * Overview Report @@ -174,11 +175,7 @@ class OverviewReportAjaxAPI extends AjaxController { $stop = $this->get('period', 'now'); } - if ($start != 'last month') - $start = DateTime::createFromFormat($cfg->getDateFormat(), - $start)->format('U'); - else - $start = strtotime($start); + $start = strtotime($start); if (substr($stop, 0, 1) == '+') $stop = strftime('%Y-%m-%d ', $start) . $stop; diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php index 8181b4eaea1646aba8e229a96a6ac8fd9d2755b2..e0330dc98daf11ec850e10a6ac859c4916c67271 100644 --- a/include/ajax.tasks.php +++ b/include/ajax.tasks.php @@ -119,7 +119,7 @@ class TasksAjaxAPI extends AjaxController { Http::response(404, __('Unknown action')); - $errors = $e = array(); + $info = $errors = $e = array(); $inc = null; $i = $count = 0; if ($_POST) { diff --git a/include/ajax.thread.php b/include/ajax.thread.php new file mode 100644 index 0000000000000000000000000000000000000000..4a8b72016d69ce2ff4565d5026134b380f6e0649 --- /dev/null +++ b/include/ajax.thread.php @@ -0,0 +1,305 @@ +<?php +/********************************************************************* + ajax.thread.php + + AJAX interface for thread + + Peter Rotich <peter@osticket.com> + Copyright (c) 2015 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +if(!defined('INCLUDE_DIR')) die('403'); + +include_once(INCLUDE_DIR.'class.ticket.php'); +require_once(INCLUDE_DIR.'class.ajax.php'); +require_once(INCLUDE_DIR.'class.note.php'); +include_once INCLUDE_DIR . 'class.thread_actions.php'; + +class ThreadAjaxAPI extends AjaxController { + + function lookup() { + global $thisstaff; + + if(!is_numeric($_REQUEST['q'])) + return self::lookupByEmail(); + + + $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; + $tickets=array(); + + $visibility = Q::any(array( + 'staff_id' => $thisstaff->getId(), + 'team_id__in' => $thisstaff->teams->values_flat('team_id'), + )); + if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) { + $visibility->add(array('dept_id__in' => $depts)); + } + + + $hits = TicketModel::objects() + ->filter(Q::any(array( + 'number__startswith' => $_REQUEST['q'], + ))) + ->filter($visibility) + ->values('number', 'user__emails__address') + ->annotate(array('tickets' => SqlAggregate::COUNT('ticket_id'))) + ->order_by('-created') + ->limit($limit); + + foreach ($hits as $T) { + $tickets[] = array('id'=>$T['number'], 'value'=>$T['number'], + 'info'=>"{$T['number']} — {$T['user__emails__address']}", + 'matches'=>$_REQUEST['q']); + } + if (!$tickets) + return self::lookupByEmail(); + + return $this->json_encode($tickets); + } + + + function addRemoteCollaborator($tid, $bk, $id) { + global $thisstaff; + + if (!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(404, 'No such thread'); + elseif (!$bk || !$id) + Http::response(422, 'Backend and user id required'); + elseif (!($backend = StaffAuthenticationBackend::getBackend($bk))) + Http::response(404, 'User not found'); + + $user_info = $backend->lookup($id); + $form = UserForm::getUserForm()->getForm($user_info); + $info = array(); + if (!$user_info) + $info['error'] = __('Unable to find user in directory'); + + return self::_addcollaborator($thread, null, $form, $info); + } + + //Collaborators utils + function addCollaborator($tid, $uid=0) { + global $thisstaff; + + if (!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(404, __('No such thread')); + + + $user = $uid? User::lookup($uid) : null; + + //If not a post then assume new collaborator form + if(!$_POST) + return self::_addcollaborator($thread, $user); + + $user = $form = null; + if (isset($_POST['id']) && $_POST['id']) { //Existing user/ + $user = User::lookup($_POST['id']); + } else { //We're creating a new user! + $form = UserForm::getUserForm()->getForm($_POST); + $user = User::fromForm($form); + } + + $errors = $info = array(); + if ($user) { + // FIXME: Refuse to add ticket owner?? + if (($c=$thread->addCollaborator($user, + array('isactive'=>1), $errors))) { + $note = Format::htmlchars(sprintf(__('%s <%s> added as a collaborator'), + Format::htmlchars($c->getName()), $c->getEmail())); + + $thread->getObject()->postThreadEntry('N', + array( + 'title' => __('New Collaborator Added'), + 'note' => $note + ), + array( + 'poster' => $thisstaff, + 'alert' => false + ) + ); + $info = array('msg' => sprintf(__('%s added as a collaborator'), + Format::htmlchars($c->getName()))); + return self::_collaborators($thread, $info); + } + } + + if($errors && $errors['err']) { + $info +=array('error' => $errors['err']); + } else { + $info +=array('error' =>__('Unable to add collaborator. Internal error')); + } + + return self::_addcollaborator($thread, $user, $form, $info); + } + + function updateCollaborator($tid, $cid) { + global $thisstaff; + + if (!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(405, 'No such thread'); + + + if (!($c=Collaborator::lookup(array( + 'id' => $cid, + 'thread_id' => $thread->getId()))) + || !($user=$c->getUser())) + Http::response(406, 'Unknown collaborator'); + + $errors = array(); + if(!$user->updateInfo($_POST, $errors)) + return self::_collaborator($c ,$user->getForms($_POST), $errors); + + $info = array('msg' => sprintf('%s updated successfully', + Format::htmlchars($c->getName()))); + + return self::_collaborators($thread, $info); + } + + function viewCollaborator($tid, $cid) { + global $thisstaff; + + if (!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(404, 'No such thread'); + + + if (!($collaborator=Collaborator::lookup(array( + 'id' => $cid, + 'thread_id' => $thread->getId())))) + Http::response(404, 'Unknown collaborator'); + + return self::_collaborator($collaborator); + } + + function showCollaborators($tid) { + global $thisstaff; + + if(!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(404, 'No such thread'); + + if ($thread->getCollaborators()) + return self::_collaborators($thread); + + return self::_addcollaborator($thread); + } + + function previewCollaborators($tid) { + global $thisstaff; + + if (!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(404, 'No such thread'); + + ob_start(); + include STAFFINC_DIR . 'templates/collaborators-preview.tmpl.php'; + $resp = ob_get_contents(); + ob_end_clean(); + + return $resp; + } + + function _addcollaborator($thread, $user=null, $form=null, $info=array()) { + global $thisstaff; + + $info += array( + 'title' => __('Add a collaborator'), + 'action' => sprintf('#thread/%d/add-collaborator', + $thread->getId()), + 'onselect' => sprintf('ajax.php/thread/%d/add-collaborator/', + $thread->getId()), + ); + + ob_start(); + include STAFFINC_DIR . 'templates/user-lookup.tmpl.php'; + $resp = ob_get_contents(); + ob_end_clean(); + + return $resp; + } + + function updateCollaborators($tid) { + global $thisstaff; + + if (!($thread=Thread::lookup($tid)) + || !($object=$thread->getObject()) + || !$object->checkStaffPerm($thisstaff)) + Http::response(404, 'No such thread'); + + $errors = $info = array(); + if ($thread->updateCollaborators($_POST, $errors)) + Http::response(201, sprintf('Recipients (%d of %d)', + $thread->getNumActiveCollaborators(), + $thread->getNumCollaborators())); + + if($errors && $errors['err']) + $info +=array('error' => $errors['err']); + + return self::_collaborators($thread, $info); + } + + + + function _collaborator($collaborator, $form=null, $info=array()) { + global $thisstaff; + + $info += array('action' => sprintf('#thread/%d/collaborators/%d', + $collaborator->thread_id, $collaborator->getId())); + + $user = $collaborator->getUser(); + + ob_start(); + include(STAFFINC_DIR . 'templates/user.tmpl.php'); + $resp = ob_get_contents(); + ob_end_clean(); + + return $resp; + } + + function _collaborators($thread, $info=array()) { + + ob_start(); + include(STAFFINC_DIR . 'templates/collaborators.tmpl.php'); + $resp = ob_get_contents(); + ob_end_clean(); + + return $resp; + } + + function triggerThreadAction($ticket_id, $thread_id, $action) { + $thread = ThreadEntry::lookup($thread_id); + if (!$thread) + Http::response(404, 'No such ticket thread entry'); + if ($thread->getThread()->getObjectId() != $ticket_id) + Http::response(404, 'No such ticket thread entry'); + + $valid = false; + foreach ($thread->getActions() as $group=>$list) { + foreach ($list as $name=>$A) { + if ($A->getId() == $action) { + $valid = true; break; + } + } + } + if (!$valid) + Http::response(400, 'Not a valid action for this thread'); + + $thread->triggerAction($action); + } +} +?> diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index d1ea440c0495351edb894f76acccb13599d670be..1e95dde3ec1c033f51fb70b13456739aafea7cfb 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -192,189 +192,6 @@ class TicketsAjaxAPI extends AjaxController { include STAFFINC_DIR . 'templates/ticket-preview.tmpl.php'; } - function addRemoteCollaborator($tid, $bk, $id) { - global $thisstaff; - - if (!($ticket=Ticket::lookup($tid)) - || !$ticket->checkStaffPerm($thisstaff)) - Http::response(404, 'No such ticket'); - elseif (!$bk || !$id) - Http::response(422, 'Backend and user id required'); - elseif (!($backend = StaffAuthenticationBackend::getBackend($bk))) - Http::response(404, 'User not found'); - - $user_info = $backend->lookup($id); - $form = UserForm::getUserForm()->getForm($user_info); - $info = array(); - if (!$user_info) - $info['error'] = __('Unable to find user in directory'); - - return self::_addcollaborator($ticket, null, $form, $info); - } - - //Collaborators utils - function addCollaborator($tid, $uid=0) { - global $thisstaff; - - if (!($ticket=Ticket::lookup($tid)) - || !$ticket->checkStaffPerm($thisstaff)) - Http::response(404, __('No such ticket')); - - - $user = $uid? User::lookup($uid) : null; - - //If not a post then assume new collaborator form - if(!$_POST) - return self::_addcollaborator($ticket, $user); - - $user = $form = null; - if (isset($_POST['id']) && $_POST['id']) { //Existing user/ - $user = User::lookup($_POST['id']); - } else { //We're creating a new user! - $form = UserForm::getUserForm()->getForm($_POST); - $user = User::fromForm($form); - } - - $errors = $info = array(); - if ($user) { - if ($user->getId() == $ticket->getOwnerId()) - $errors['err'] = sprintf(__('Ticket owner, %s, is a collaborator by default!'), - Format::htmlchars($user->getName())); - elseif (($c=$ticket->addCollaborator($user, - array('isactive'=>1), $errors))) { - $note = Format::htmlchars(sprintf(__('%s <%s> added as a collaborator'), - Format::htmlchars($c->getName()), $c->getEmail())); - $ticket->logNote(__('New Collaborator Added'), $note, - $thisstaff, false); - $info = array('msg' => sprintf(__('%s added as a collaborator'), - Format::htmlchars($c->getName()))); - return self::_collaborators($ticket, $info); - } - } - - if($errors && $errors['err']) { - $info +=array('error' => $errors['err']); - } else { - $info +=array('error' =>__('Unable to add collaborator. Internal error')); - } - - return self::_addcollaborator($ticket, $user, $form, $info); - } - - function updateCollaborator($cid) { - global $thisstaff; - - if(!($c=Collaborator::lookup($cid)) - || !($user=$c->getUser()) - || !($ticket=$c->getTicket()) - || !$ticket->checkStaffPerm($thisstaff) - ) - Http::response(404, 'Unknown collaborator'); - - $errors = array(); - if(!$user->updateInfo($_POST, $errors)) - return self::_collaborator($c ,$user->getForms($_POST), $errors); - - $info = array('msg' => sprintf('%s updated successfully', - Format::htmlchars($c->getName()))); - - return self::_collaborators($ticket, $info); - } - - function viewCollaborator($cid) { - global $thisstaff; - - if(!($collaborator=Collaborator::lookup($cid)) - || !($ticket=$collaborator->getTicket()) - || !$ticket->checkStaffPerm($thisstaff)) - Http::response(404, 'Unknown collaborator'); - - return self::_collaborator($collaborator); - } - - function showCollaborators($tid) { - global $thisstaff; - - if(!($ticket=Ticket::lookup($tid)) - || !$ticket->checkStaffPerm($thisstaff)) - Http::response(404, 'No such ticket'); - - if($ticket->getCollaborators()) - return self::_collaborators($ticket); - - return self::_addcollaborator($ticket); - } - - function previewCollaborators($tid) { - global $thisstaff; - - if (!($ticket=Ticket::lookup($tid)) - || !$ticket->checkStaffPerm($thisstaff)) - Http::response(404, 'No such ticket'); - - ob_start(); - include STAFFINC_DIR . 'templates/collaborators-preview.tmpl.php'; - $resp = ob_get_contents(); - ob_end_clean(); - - return $resp; - } - - function _addcollaborator($ticket, $user=null, $form=null, $info=array()) { - - $info += array( - 'title' => sprintf(__('Ticket #%s: Add a collaborator'), $ticket->getNumber()), - 'action' => sprintf('#tickets/%d/add-collaborator', $ticket->getId()), - 'onselect' => sprintf('ajax.php/tickets/%d/add-collaborator/', $ticket->getId()), - ); - return self::_userlookup($user, $form, $info); - } - - - function updateCollaborators($tid) { - global $thisstaff; - - if(!($ticket=Ticket::lookup($tid)) - || !$ticket->checkStaffPerm($thisstaff)) - Http::response(404, 'No such ticket'); - - $errors = $info = array(); - if ($ticket->updateCollaborators($_POST, $errors)) - Http::response(201, sprintf('Recipients (%d of %d)', - $ticket->getNumActiveCollaborators(), - $ticket->getNumCollaborators())); - - if($errors && $errors['err']) - $info +=array('error' => $errors['err']); - - return self::_collaborators($ticket, $info); - } - - - - function _collaborator($collaborator, $form=null, $info=array()) { - - $info += array('action' => '#collaborators/'.$collaborator->getId()); - - $user = $collaborator->getUser(); - - ob_start(); - include(STAFFINC_DIR . 'templates/user.tmpl.php'); - $resp = ob_get_contents(); - ob_end_clean(); - - return $resp; - } - - function _collaborators($ticket, $info=array()) { - - ob_start(); - include(STAFFINC_DIR . 'templates/collaborators.tmpl.php'); - $resp = ob_get_contents(); - ob_end_clean(); - - return $resp; - } function viewUser($tid) { global $thisstaff; diff --git a/include/class.attachment.php b/include/class.attachment.php index 748266003819edd0fbde1f248b0afdf15657a8a8..3247d06912d8baafaa895843556b6236f809a35d 100644 --- a/include/class.attachment.php +++ b/include/class.attachment.php @@ -22,10 +22,10 @@ class Attachment extends VerySimpleModel { 'pk' => array('id'), 'select_related' => array('file'), 'joins' => array( - 'thread_entry' => array( + 'draft' => array( 'constraint' => array( - 'type' => "'H'", - 'object_id' => 'ThreadEntry.id', + 'type' => "'D'", + 'object_id' => 'Draft.id', ), ), 'file' => array( @@ -33,6 +33,12 @@ class Attachment extends VerySimpleModel { 'file_id' => 'AttachmentFile.id', ), ), + 'thread_entry' => array( + 'constraint' => array( + 'type' => "'H'", + 'object_id' => 'ThreadEntry.id', + ), + ), ), ); diff --git a/include/class.client.php b/include/class.client.php index 25b03281b9abd31d359d265592dcdc7058061a24..eb4ef00a870c62a6355d3269ec5b6c0476ed27c5 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -245,6 +245,19 @@ class EndUser extends BaseAuthenticatedUser { return ($stats=$this->getTicketStats())?$stats['closed']:0; } + function getNumOrganizationTickets() { + if (!($stats=$this->getTicketStats())) + return 0; + + return $stats['org-open']+$stats['org-closed']; + } + function getNumOpenOrganizationTickets() { + return ($stats=$this->getTicketStats())?$stats['org-open']:0; + } + function getNumClosedOrganizationTickets() { + return ($stats=$this->getTicketStats())?$stats['org-closed']:0; + } + function getAccount() { if ($this->_account === false) $this->_account = @@ -263,6 +276,8 @@ class EndUser extends BaseAuthenticatedUser { $where = ' WHERE ticket.user_id = '.db_input($this->getId()) .' OR collab.user_id = '.db_input($this->getId()).' '; + $where2 = ' WHERE user.org_id > 0 AND user.org_id = '.db_input($this->getOrgId()).' '; + $join = 'LEFT JOIN '.THREAD_TABLE.' thread ON (ticket.ticket_id = thread.object_id and thread.object_type = \'T\') LEFT JOIN '.THREAD_COLLABORATOR_TABLE.' collab @@ -283,7 +298,25 @@ class EndUser extends BaseAuthenticatedUser { ON (ticket.status_id=status.id AND status.state=\'closed\' ) ' . $join - . $where; + . $where + + .'UNION SELECT \'org-open\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'INNER JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id) ' + .'INNER JOIN '.TICKET_STATUS_TABLE. ' status + ON (ticket.status_id=status.id + AND status.state=\'open\' ) ' + . $join + . $where2 + + .'UNION SELECT \'org-closed\', count( ticket.ticket_id ) AS tickets ' + .'FROM ' . TICKET_TABLE . ' ticket ' + .'INNER JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id) ' + .'INNER JOIN '.TICKET_STATUS_TABLE. ' status + ON (ticket.status_id=status.id + AND status.state=\'closed\' ) ' + . $join + . $where2; $res = db_query($sql); $stats = array(); diff --git a/include/class.collaborator.php b/include/class.collaborator.php index c457f1cd3cf4feac927e6076a428bc6a90ca4766..205b5200b151911c757611483fae9560f041bb27 100644 --- a/include/class.collaborator.php +++ b/include/class.collaborator.php @@ -51,6 +51,10 @@ implements EmailContact, ITicketUser { return $this->created; } + function getThreadId() { + return $this->thread_id; + } + function getTicketId() { if ($this->thread->object_type == ObjectModel::OBJECT_TYPE_TICKET) return $this->thread->object_id; @@ -140,18 +144,5 @@ implements EmailContact, ITicketUser { return false; } - static function forThread($tid, $criteria=array()) { - - $collaborators = static::objects() - ->filter(array('thread_id' => $tid)); - - if (isset($criteria['isactive'])) - $collaborators->filter(array('isactive' => $criteria['isactive'])); - - // TODO: sort by name of the user - $collaborators->order_by('user__name'); - - return $collaborators; - } } ?> diff --git a/include/class.config.php b/include/class.config.php index a85fc696263e4316da1f0181e6ccf6a4ea6921ce..cb1f15d5c4f96447db712798a2a6a8bcfbd548ec 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -999,12 +999,6 @@ class OsticketConfig extends Config { case 'access': return $this->updateAccessSettings($vars, $errors); break; - case 'autoresp': - return $this->updateAutoresponderSettings($vars, $errors); - break; - case 'alerts': - return $this->updateAlertsSettings($vars, $errors); - break; case 'kb': return $this->updateKBSettings($vars, $errors); break; @@ -1114,6 +1108,9 @@ class OsticketConfig extends Config { if (!preg_match('`(?!<\\\)#`', $vars['ticket_number_format'])) $errors['ticket_number_format'] = 'Ticket number format requires at least one hash character (#)'; + $this->updateAutoresponderSettings($vars, $errors); + $this->updateAlertsSettings($vars, $errors); + if(!Validator::process($f, $vars, $errors) || $errors) return false; diff --git a/include/class.dept.php b/include/class.dept.php index 8343d6f6690c9917a8fbe8f4e145d7a5f08e03bd..4e42d9335fca4fe40f339266012c8f400868100e 100644 --- a/include/class.dept.php +++ b/include/class.dept.php @@ -159,6 +159,7 @@ implements TemplateVariable { if (!$this->_members || $criteria) { $members = Staff::objects() + ->distinct('staff_id') ->filter(Q::any(array( 'dept_id' => $this->getId(), new Q(array( diff --git a/include/class.draft.php b/include/class.draft.php index 5b2d8462bc5515eec588df3fed2c05aa8fe7294d..1938064f8bfe5dd605bfc1c56077f0be58e06bd1 100644 --- a/include/class.draft.php +++ b/include/class.draft.php @@ -167,14 +167,12 @@ class Draft extends VerySimpleModel { * are cleaned up. */ static function deleteForNamespace($namespace, $staff_id=false) { - $sql = 'DELETE attach FROM '.ATTACHMENT_TABLE.' attach - INNER JOIN '.DRAFT_TABLE.' draft - ON (attach.object_id = draft.id AND attach.`type`=\'D\') - WHERE draft.`namespace` LIKE '.db_input($namespace); + $attachments = Attachment::objects() + ->filter(array('draft__namespace__like' => $namespace)); if ($staff_id) - $sql .= ' AND draft.staff_id='.db_input($staff_id); - if (!db_query($sql)) - return false; + $attachments->filter(array('draft__staff_id' => $staff_id)); + + $attachments->delete(); $criteria = array('namespace__like'=>$namespace); if ($staff_id) @@ -183,11 +181,10 @@ class Draft extends VerySimpleModel { } static function cleanup() { - // Keep client drafts for two weeks (14 days) + // Keep drafts for two weeks (14 days) $sql = 'DELETE FROM '.DRAFT_TABLE - ." WHERE `namespace` LIKE 'ticket.client.%' - AND ((updated IS NULL AND datediff(now(), created) > 14) - OR datediff(now(), updated) > 14)"; + ." WHERE (updated IS NULL AND datediff(now(), created) > 14) + OR datediff(now(), updated) > 14"; return db_query($sql); } } diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index ca2b3f2c8fa2a649f5b9cfa2e55a786737339f38..43ae645c765a97524d7479e6995c5261319d522d 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -153,6 +153,14 @@ class DynamicForm extends VerySimpleModel { return $inst; } + function disableFields(array $ids) { + foreach ($this->getFields() as $F) { + if (in_array($F->get('id'), $ids)) { + $F->disable(); + } + } + } + function getTranslateTag($subtag) { return _H(sprintf('form.%s.%s', $subtag, $this->id)); } @@ -236,7 +244,7 @@ class DynamicForm extends VerySimpleModel { // New translations (?) if ($vars['trans'] && is_array($vars['trans'])) { foreach ($vars['trans'] as $lang=>$parts) { - if (!Internationalization::isLanguageInstalled($lang)) + if (!Internationalization::isLanguageEnabled($lang)) continue; foreach ($parts as $T => $content) { $content = trim($content); @@ -471,7 +479,8 @@ class TicketForm extends DynamicForm { // field.id=ans.field_id // where entry.object_type='T' group by entry.object_id; $sql = 'CREATE TABLE `'.TABLE_PREFIX.'ticket__cdata` (PRIMARY KEY - (ticket_id)) AS ' . static::getCrossTabQuery('T', 'ticket_id'); + (ticket_id)) DEFAULT CHARSET=utf8 AS ' + . static::getCrossTabQuery('T', 'ticket_id'); db_query($sql); } @@ -985,15 +994,18 @@ class DynamicFormEntry extends VerySimpleModel { function getForm() { if (!isset($this->_form)) { // XXX: Should source be $this? - $form = new SimpleForm($this->getFields(), $this->getSource(), + $fields = $this->getFields(); + if (isset($this->extra)) { + $x = JsonDataParser::decode($this->extra) ?: array(); + foreach ($x['disable'] ?: array() as $id) { + unset($fields[$id]); + } + } + $form = new SimpleForm($fields, $this->getSource(), array( 'title' => $this->getTitle(), 'instructions' => $this->getInstructions(), )); - if (isset($this->extra)) { - $x = JsonDataParser::decode($this->extra) ?: array(); - $form->disableFields($x['disable'] ?: array()); - } $this->_form = $form; } return $this->_form; diff --git a/include/class.export.php b/include/class.export.php index 447435b41e8cf7d99a979b3702c966e0dc9f32d8..acdc333582c6d7d54b35c0fd17d4db5ccd5ec473 100644 --- a/include/class.export.php +++ b/include/class.export.php @@ -110,6 +110,10 @@ class Export { return false; } + static function saveTasks($sql, $filename, $how='csv') { + return false; + } + static function saveUsers($sql, $filename, $how='csv') { $exclude = array('name', 'email'); diff --git a/include/class.faq.php b/include/class.faq.php index 20474c7331078190ecf8fed65cc755db21dd96b3..f2d42355bff78ea3781f62a4b1ca4b9b8e663421 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -316,8 +316,8 @@ class FAQ extends VerySimpleModel { function getVisibleAttachments() { return array_merge( - $this->attachments->getSeparates() ?: array(), - $this->getLocalAttachments()); + $this->attachments->getSeparates()->all() ?: array(), + $this->getLocalAttachments()->all()); } function getAttachmentsLinks($separator=' ',$target='') { diff --git a/include/class.file.php b/include/class.file.php index 78cbed9c8e9d634cf828a34d5ab28c532804c3fb..5f4013ff762f85b780c61bc746d84190deba714b 100644 --- a/include/class.file.php +++ b/include/class.file.php @@ -437,6 +437,10 @@ class AttachmentFile extends VerySimpleModel { return $f; } + static function __create($file, &$errors) { + return static::create($file); + } + /** * Migrate this file from the current backend to the backend specified. * diff --git a/include/class.format.php b/include/class.format.php index 06a07f5065e5ad5bcc5357833817eb9d37544fdd..cd9b27f24657c5c95ff59699d9706912c38ae36b 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -366,7 +366,7 @@ class Format { }, 'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; src: cid, http, https, data', 'elements' => '*+iframe', - 'spec' => 'span=data-src,width,height', + 'spec' => 'span=data-src,width,height;img=data-cid', ); return Format::html($text, $config); } @@ -441,9 +441,12 @@ class Format { $strftimeFallback, $timezone, $user=false) { global $cfg; - if ($timestamp && $fromDb) { + if (!$timestamp) + return ''; + + if ($fromDb) $timestamp = Misc::db2gmtime($timestamp); - } + if (class_exists('IntlDateFormatter')) { $formatter = new IntlDateFormatter( Internationalization::getCurrentLocale($user), diff --git a/include/class.forms.php b/include/class.forms.php index 9b8de17b2bee8cf3f49853c3823cd36a1adcaa63..0b6da8f275f0f5a75fc08c3f618279bdd2aff058 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -56,7 +56,7 @@ class Form { function setFields($fields) { - if (!is_array($fields)) + if (!is_array($fields) && !$fields instanceof Traversable) return; $this->fields = $fields; @@ -127,14 +127,6 @@ class Form { return $this->_clean; } - function disableFields(array $ids) { - foreach ($this->getFields() as $F) { - if (in_array($F->get('id'), $ids)) { - $F->disable(); - } - } - } - function errors($formOnly=false) { return ($formOnly) ? $this->_errors['form'] : $this->_errors; } @@ -438,7 +430,7 @@ class FormField { return $this->_errors; } function addError($message, $index=false) { - if ($field) + if ($index) $this->_errors[$index] = $message; else $this->_errors[] = $message; @@ -1622,6 +1614,14 @@ class ThreadEntryField extends FormField { return false; } + function getMedia() { + $config = $this->getConfiguration(); + $media = parent::getMedia() ?: array(); + if ($config['attachments']) + $media = array_merge_recursive($media, FileUploadWidget::$media); + return $media; + } + function getConfigurationOptions() { global $cfg; @@ -2576,10 +2576,17 @@ class TextboxWidget extends Widget { $disabled = 'disabled="disabled"'; if (isset($config['translatable']) && $config['translatable']) $translatable = 'data-translate-tag="'.$config['translatable'].'"'; + $type = static::$input_type; + $types = array( + 'email' => 'email', + 'phone' => 'tel', + ); + if ($type == 'text' && isset($types[$config['validator']])) + $type = $types[$config['validator']]; $placeholder = sprintf('placeholder="%s"', $this->field->getLocal('placeholder', $config['placeholder'])); ?> - <input type="<?php echo static::$input_type; ?>" + <input type="<?php echo $type; ?>" id="<?php echo $this->id; ?>" <?php echo implode(' ', array_filter(array( $size, $maxlength, $classes, $autocomplete, $disabled, @@ -2669,7 +2676,7 @@ class PhoneNumberWidget extends Widget { $config = $this->field->getConfiguration(); list($phone, $ext) = explode("X", $this->value); ?> - <input id="<?php echo $this->id; ?>" type="text" name="<?php echo $this->name; ?>" value="<?php + <input id="<?php echo $this->id; ?>" type="tel" name="<?php echo $this->name; ?>" value="<?php echo Format::htmlchars($phone); ?>"/><?php // Allow display of extension field even if disabled if the phone // number being edited has an extension @@ -2745,13 +2752,13 @@ class ChoicesWidget extends Widget { id="<?php echo $this->id; ?>" data-placeholder="<?php echo $prompt; ?>" <?php if ($config['multiselect']) - echo ' multiple="multiple" class="chosen-select"'; ?>> + echo ' multiple="multiple"'; ?>> <?php if (!$have_def && !$config['multiselect']) { ?> <option value="<?php echo $def_key; ?>">— <?php echo $def_val; ?> —</option> <?php } - $this->emitChoices($choices, $values); ?> + $this->emitChoices($choices, $values, $have_def, $def_key); ?> </select> <?php if ($config['multiselect']) { @@ -2759,17 +2766,17 @@ class ChoicesWidget extends Widget { <script type="text/javascript"> $(function() { $("#<?php echo $this->id; ?>") - .chosen({'disable_search_threshold':10, 'width': '250px'}); + .select2({'minimumResultsForSearch':10, 'width': '350px'}); }); </script> <?php } } - function emitChoices($choices, $values=array()) { + function emitChoices($choices, $values=array(), $have_def=false, $def_key=null) { reset($choices); if (is_array(current($choices)) || current($choices) instanceof Traversable) - return $this->emitComplexChoices($choices, $values); + return $this->emitComplexChoices($choices, $values, $have_def, $def_key); foreach ($choices as $key => $name) { if (!$have_def && $key == $def_key) @@ -2781,7 +2788,7 @@ class ChoicesWidget extends Widget { } } - function emitComplexChoices($choices, $values=array()) { + function emitComplexChoices($choices, $values=array(), $have_def=false, $def_key=null) { foreach ($choices as $label => $group) { ?> <optgroup label="<?php echo $label; ?>"><?php foreach ($group as $key => $name) { @@ -3307,7 +3314,7 @@ class AssignmentForm extends Form { 'default'=>'', 'configuration' => array( 'html' => true, - 'size' => 'large', + 'size' => 'small', 'placeholder' => __('Optional reason for the assignment'), ), ) diff --git a/include/class.i18n.php b/include/class.i18n.php index c6e4bfc61b50973d19a3a2ad38d140178c2ef49f..5d5000b2a9b06bafbdad7025ea918f2308d7560e 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -66,7 +66,7 @@ class Internationalization { 'role.yaml' => 'Role::__create', // Note that group requires department 'group.yaml' => 'Group::__create', - 'file.yaml' => 'AttachmentFile::create', + 'file.yaml' => 'AttachmentFile::__create', 'sequence.yaml' => 'Sequence::__create', ); @@ -255,18 +255,28 @@ class Internationalization { return isset($langs[strtolower($code)]); } + static function isLanguageEnabled($code) { + $langs = self::getConfiguredSystemLanguages(); + return isset($langs[$code]); + } + static function getConfiguredSystemLanguages() { global $cfg; + static $langs; if (!$cfg) return self::availableLanguages(); - $pri = $cfg->getPrimaryLanguage(); - $langs = array($pri => self::getLanguageInfo($pri)); + if (!isset($langs)) { + $pri = $cfg->getPrimaryLanguage(); + if ($info = self::getLanguageInfo($pri)) + $langs = array($pri => $info); - // Honor sorting preference of ::availableLanguages() - foreach ($cfg->getSecondaryLanguages() as $l) { - $langs[$l] = self::getLanguageInfo($l); + // Honor sorting preference of ::availableLanguages() + foreach ($cfg->getSecondaryLanguages() as $l) { + if ($info = self::getLanguageInfo($l)) + $langs[$l] = $info; + } } return $langs; } @@ -362,19 +372,15 @@ class Internationalization { static function getCurrentLanguage($user=false) { global $thisstaff, $thisclient; - static $session = null; - - if (!isset($session)) - $session = &$_SESSION['::lang']; $user = $user ?: $thisstaff ?: $thisclient; if ($user && method_exists($user, 'getLanguage')) - if ($lang = $user->getLanguage()) + if (($lang = $user->getLanguage()) && self::isLanguageEnabled($lang)) return $lang; // Support the flag buttons for guests - if ((!$user || $user != $thisstaff) && $session) - return $session; + if ((!$user || $user != $thisstaff) && $_SESSION['::lang']) + return $_SESSION['::lang']; return self::getDefaultLanguage(); } @@ -398,6 +404,15 @@ class Internationalization { return $locale; } + static function rfc1766($what) { + if (is_array($what)) + return array_map(array(get_called_class(), 'rfc1766'), $what); + + $lr = explode('_', $what); + if (isset($lr[1])) + $lr[1] = strtoupper($lr[1]); + return implode('-', $lr); + } static function getTtfFonts() { if (!class_exists('Phar')) diff --git a/include/class.mailer.php b/include/class.mailer.php index 412428eccdf11d370951141fc91e3cd358ab5386..83f097050624924326e9da6b22e90eeb041f5deb 100644 --- a/include/class.mailer.php +++ b/include/class.mailer.php @@ -509,6 +509,8 @@ class Mailer { //No SMTP or it failed....use php's native mail function. $mail = mail::factory('mail'); + // Ensure the To: header is properly encoded. + $to = $headers['To']; return PEAR::isError($mail->send($to, $headers, $body))?false:$messageId; } diff --git a/include/class.nav.php b/include/class.nav.php index 7d3c119b753d515ddf3e24a76359627eec9723c2..e6df1e897ea390b2d59b4dc38de5edfcdf11ae18 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -249,8 +249,6 @@ class AdminNav extends StaffNav{ $subnav[]=array('desc'=>__('Tasks'),'href'=>'settings.php?t=tasks','iconclass'=>'lists'); $subnav[]=array('desc'=>__('Access'),'href'=>'settings.php?t=access','iconclass'=>'users'); $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 and Notices'),'href'=>'settings.php?t=alerts','iconclass'=>'alert-settings'); break; case 'manage': $subnav[]=array('desc'=>__('Help Topics'),'href'=>'helptopics.php','iconclass'=>'helpTopics'); diff --git a/include/class.orm.php b/include/class.orm.php index 38e1d931d49db8057591de5d9d250e911519d3f0..57029871265b38038f32d5726038a724d39d3de0 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -47,10 +47,10 @@ class ModelMeta implements ArrayAccess { if (!$meta['table']) throw new OrmConfigurationException( - __('Model does not define meta.table'), $model); + sprintf(__('%s: Model does not define meta.table'), $model)); elseif (!$meta['pk']) throw new OrmConfigurationException( - __('Model does not define meta.pk'), $model); + sprintf(__('%s: Model does not define meta.pk'), $model)); // Ensure other supported fields are set and are arrays foreach (array('pk', 'ordering', 'defer') as $f) { @@ -141,7 +141,20 @@ class ModelMeta implements ArrayAccess { } function inspectFields() { - return DbEngine::getCompiler()->inspectTable($this['table']); + static $cache; + if (!isset($cache)) + $cache = function_exists('apc_fetch'); + if ($cache) { + $key = md5(SECRET_SALT . GIT_VERSION . $this['table']); + if ($fields = apc_fetch($key)) { + return $fields; + } + } + $fields = DbEngine::getCompiler()->inspectTable($this['table']); + if ($cache) { + apc_store($key, $fields); + } + return $fields; } } @@ -172,10 +185,17 @@ class VerySimpleModel { $j = static::$meta['joins'][$field]; // Support instrumented lists and such if (isset($j['list']) && $j['list']) { - $fkey = $j['fkey']; + $class = $j['fkey'][0]; + $fkey = array(); + // Localize the foreign key constraint + foreach ($j['constraint'] as $local=>$foreign) { + list($_klas,$F) = $foreign; + $fkey[$F ?: $_klas] = ($local[0] == "'") + ? trim($local, "'") : $this->ht[$local]; + } $v = $this->ht[$field] = new InstrumentedList( - // Send Model, Foriegn-Field, Local-Id - array($fkey[0], $fkey[1], $this->get($j['local'])) + // Send Model, [Foriegn-Field => Local-Id] + array($class, $fkey) ); return $v; } @@ -1349,29 +1369,32 @@ class HashArrayIterator extends ResultSet { class InstrumentedList extends ModelInstanceManager { var $key; - var $id; var $model; function __construct($fkey, $queryset=false) { - list($model, $this->key, $this->id) = $fkey; + list($model, $this->key) = $fkey; if (!$queryset) - $queryset = $model::objects()->filter(array($this->key=>$this->id)); + $queryset = $model::objects()->filter($this->key); parent::__construct($queryset); $this->model = $model; - if (!$this->id) - $this->resource = null; } function add($object, $at=false) { if (!$object || !$object instanceof $this->model) throw new Exception(__('Attempting to add invalid object to list')); - $object->set($this->key, $this->id); + foreach ($this->key as $field=>$value) + $object->set($field, $value); + + if (!$object->__new__) + $object->save(); if ($at !== false) $this->cache[$at] = $object; else $this->cache[] = $object; + + return $object; } function remove($object, $delete=true) { if ($delete) @@ -2298,6 +2321,10 @@ class MySqlExecutor { $types .= 's'; $p = $p->format('Y-m-d h:i:s'); } + elseif (is_object($p)) { + $types .= 's'; + $p = (string) $p; + } // TODO: Emit error if param is null $ps[] = &$p; } diff --git a/include/class.osticket.php b/include/class.osticket.php index 75a247c1072a7cf3143c8929a4ef46e13b228d1b..2f06453973fc318c303a76edb18cb5b3195ff745 100644 --- a/include/class.osticket.php +++ b/include/class.osticket.php @@ -270,6 +270,10 @@ class osTicket { $e->getTraceAsString()); $error .= nl2br("\n\n---- "._S('Backtrace')." ----\n".$bt); + // Prevent recursive loops through this code path + if (substr_count($bt, __FUNCTION__) > 1) + return; + return $this->log(LOG_ERR, $title, $error, $alert); } diff --git a/include/class.search.php b/include/class.search.php index d41f0a59a3a6a871fef236d018cbdde2d3555ffb..b319b0a587bfa08b6f5acf8f4a3a1a7cca38fe76 100644 --- a/include/class.search.php +++ b/include/class.search.php @@ -82,16 +82,19 @@ class SearchInterface { } function update($model, $id, $content, $new=false, $attrs=array()) { - if (!$this->backend) - return; - - $this->backend->update($model, $id, $content, $new, $attrs); + if ($this->backend) + $this->backend->update($model, $id, $content, $new, $attrs); } function createModel($model) { return $this->updateModel($model, true); } + function deleteModel($model) { + if ($this->backend) + $this->backend->delete($model); + } + function updateModel($model, $new=false) { // The MySQL backend does not need to index attributes of the // various models, because those other attributes are available in @@ -104,11 +107,10 @@ class SearchInterface { break; $this->update($model, $model->getId(), - $model->getBody()->getSearchable(), $new, + $model->getBody()->getSearchable(), + $new === true, array( 'title' => $model->getTitle(), - //TODO: send attribute of object_id - 'ticket_id' => $model->getThread()->getObjectId(), 'created' => $model->getCreateDate(), ) ); @@ -121,7 +123,7 @@ class SearchInterface { $cdata[] = $v; $this->update($model, $model->getId(), trim(implode("\n", $cdata)), - $new, + $new === true, array( 'title'=> Format::searchable($model->getSubject()), 'number'=> $model->getNumber(), @@ -148,7 +150,7 @@ class SearchInterface { $cdata[] = $v; $this->update($model, $model->getId(), trim(implode("\n", $cdata)), - $new, + $new === true, array( 'title'=> Format::searchable($model->getFullName()), 'emails'=> $model->emails->asArray(), @@ -166,7 +168,7 @@ class SearchInterface { $cdata[] = $v; $this->update($model, $model->getId(), trim(implode("\n", $cdata)), - $new, + $new === true, array( 'title'=> Format::searchable($model->getName()), 'created'=> $model->getCreateDate(), @@ -177,7 +179,7 @@ class SearchInterface { case $model instanceof FAQ: $this->update($model, $model->getId(), $model->getSearchableAnswer(), - $new, + $new === true, array( 'title'=> Format::searchable($model->getQuestion()), 'keywords'=> $model->getKeywords(), @@ -219,7 +221,7 @@ class SearchInterface { Signal::connect('model.created', array($this, 'createModel'), 'FAQ'); Signal::connect('model.updated', array($this, 'updateModel')); - #Signal::connect('model.deleted', array($this, 'deleteModel')); + Signal::connect('model.deleted', array($this, 'deleteModel')); } } @@ -239,6 +241,7 @@ class MysqlSearchBackend extends SearchBackend { // Only index 20 batches per cron run var $max_batches = 60; var $_reindexed = 0; + var $SEARCH_TABLE; function __construct() { $this->SEARCH_TABLE = TABLE_PREFIX . '_search'; @@ -257,8 +260,6 @@ class MysqlSearchBackend extends SearchBackend { } function update($model, $id, $content, $new=false, $attrs=array()) { - - if (!($type=ObjectModel::getType($model))) return; @@ -271,6 +272,8 @@ class MysqlSearchBackend extends SearchBackend { if (!$content && !$title) return; + if (!$id) + return; $sql = 'REPLACE INTO '.$this->SEARCH_TABLE . ' SET object_type='.db_input($type) @@ -280,6 +283,26 @@ class MysqlSearchBackend extends SearchBackend { return db_query($sql); } + function delete($model) { + switch (true) { + case $model instanceof Thread: + $sql = 'DELETE s.* FROM '.$this->SEARCH_TABLE + . " s JOIN ".THREAD_ENTRY_TABLE." h ON (h.id = s.object_id) " + . " WHERE s.object_type='H'" + . ' AND h.thread_id='.db_input($model->getId()); + return db_query($sql); + + default: + if (!($type = ObjectModel::getType($model))) + return; + + $sql = 'DELETE FROM '.$this->SEARCH_TABLE + . ' WHERE object_type='.db_input($type) + . ' AND object_id='.db_input($model->getId()); + return db_query($sql); + } + } + // Quote things like email addresses function quote($query) { $parts = array(); diff --git a/include/class.staff.php b/include/class.staff.php index 8362656a1370ba407e864baa49b0510cc995b25d..b573eaa6efcc7da49c0cc3a71b979a80cff9f40b 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -49,7 +49,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable { var $authkey; var $departments; - var $timezone; var $stats = array(); var $_extra; var $passwd_change; @@ -604,9 +603,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable { if($errors) return false; - $_SESSION['staff:lang'] = null; - TextDomain::configureForUser($this); - $this->firstname = $vars['firstname']; $this->lastname = $vars['lastname']; $this->email = $vars['email']; @@ -623,6 +619,9 @@ implements AuthenticatedUser, EmailContact, TemplateVariable { $this->default_paper_size = $vars['default_paper_size']; $this->lang = $vars['lang']; + $_SESSION['::lang'] = null; + TextDomain::configureForUser($this); + return $this->save(); } diff --git a/include/class.task.php b/include/class.task.php index c629379f6e88470408ab6eab9e36467576a38958..dd7fadcbd25f560b55904ea994f1862f59273919 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -181,7 +181,7 @@ class TaskModel extends VerySimpleModel { RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions()); -class Task extends TaskModel { +class Task extends TaskModel implements Threadable { var $form; var $entry; @@ -269,6 +269,10 @@ class Task extends TaskModel { return $assignees ? implode($glue, $assignees):''; } + function getThreadId() { + return $this->thread->getId(); + } + function getThread() { return $this->thread; } @@ -284,6 +288,17 @@ class Task extends TaskModel { return $thread; } + function postThreadEntry($type, $vars, $options=array()) { + $errors = array(); + $poster = isset($options['poster']) ? $options['poster'] : null; + $alert = isset($options['alert']) ? $options['alert'] : true; + switch ($type) { + case 'N': + default: + return $this->postNote($vars, $errors, $poster, $alert); + } + } + function getForm() { if (!isset($this->form)) { // Look for the entry first diff --git a/include/class.thread.php b/include/class.thread.php index bd50ecb20c48a8c7e3a2e935cfdf878042c07748..75d5a14a396bf1830b0708f89f6a6d0f7caeb72d 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -46,6 +46,7 @@ class Thread extends VerySimpleModel { ); var $_object; + var $_collaborators; // Cache for collabs function getId() { return $this->id; @@ -89,6 +90,108 @@ class Thread extends VerySimpleModel { return $base; } + // Collaborators + function getNumCollaborators() { + return $this->collaborators->count(); + } + + function getNumActiveCollaborators() { + + if (!isset($this->ht['active_collaborators'])) + $this->ht['active_collaborators'] = count($this->getActiveCollaborators()); + + return $this->ht['active_collaborators']; + } + + function getActiveCollaborators() { + return $this->getCollaborators(array('isactive'=>1)); + } + + function getCollaborators($criteria=array()) { + + if ($this->_collaborators && !$criteria) + return $this->_collaborators; + + $collaborators = $this->collaborators + ->filter(array('thread_id' => $this->getId())); + + if (isset($criteria['isactive'])) + $collaborators->filter(array('isactive' => $criteria['isactive'])); + + // TODO: sort by name of the user + $collaborators->order_by('user__name'); + + if (!$criteria) + $this->_collaborators = $collaborators; + + return $collaborators; + } + + function addCollaborator($user, $vars, &$errors) { + + if (!$user) + return null; + + $vars = array_merge(array( + 'threadId' => $this->getId(), + 'userId' => $user->getId()), $vars); + if (!($c=Collaborator::add($vars, $errors))) + return null; + + $this->_collaborators = null; + + return $c; + } + + function updateCollaborators($vars, &$errors) { + global $thisstaff; + + if (!$thisstaff) return; + + //Deletes + if($vars['del'] && ($ids=array_filter($vars['del']))) { + $collabs = array(); + foreach ($ids as $k => $cid) { + if (($c=Collaborator::lookup($cid)) + && $c->getThreadId() == $this->getId() + && $c->delete()) + $collabs[] = $c; + } + + $this->getObject()->postThreadEntry('N', + array( + 'title' => _S('Collaborators Removed'), + 'note' => implode("<br>", $collabs))); + } + + //statuses + $cids = null; + if($vars['cid'] && ($cids=array_filter($vars['cid']))) { + $this->collaborators->filter(array( + 'thread_id' => $this->getId(), + 'id__in' => $cids + ))->update(array( + 'updated' => SqlFunction::NOW(), + 'isactive' => 1, + )); + } + + if ($cids) { + $this->collaborators->filter(array( + 'thread_id' => $this->getId(), + Q::not(array('id__in' => $cids)) + ))->update(array( + 'updated' => SqlFunction::NOW(), + 'isactive' => 0, + )); + } + + unset($this->ht['active_collaborators']); + $this->_collaborators = null; + + return true; + } + // Render thread function render($type=false) { $entries = $this->getEntries(); @@ -1878,6 +1981,8 @@ abstract class ThreadEntryAction { } interface Threadable { - function postThreadEntry($type, $vars); + function getThreadId(); + function getThread(); + function postThreadEntry($type, $vars, $options=array()); } ?> diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php index 30ddc810c056e7edfee8949820f4f2665d9501a3..15758498fdabb968929300fded2c0402f3af7610 100644 --- a/include/class.thread_actions.php +++ b/include/class.thread_actions.php @@ -155,6 +155,8 @@ JS ) { // Replace previous edit -------------------------- $original = $old->getParent(); + // Link the new entry to the old id + $entry->pid = $old->pid; // Drop the previous edit, and base this edit off the original $old->delete(); $old = $original; @@ -221,9 +223,11 @@ class TEA_OrigThreadEntry extends ThreadEntryAction { } private function trigger__get() { - $entry = $this->entry->getParent(); - if (!$entry) + global $thisstaff; + + if (!$this->entry->getParent()) Http::response(404, 'No history for this entry'); + $entry = $this->entry; include STAFFINC_DIR . 'templates/thread-entry-view.tmpl.php'; } } diff --git a/include/class.ticket.php b/include/class.ticket.php index d114d1664665c5444e6ddd3c41c692f88f25b83e..605758114102bcc1dfe959abec689b933a8e4fb9 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -883,35 +883,6 @@ implements RestrictedAccess, Threadable, TemplateVariable { return $entries; } - //Collaborators - function getNumCollaborators() { - return count($this->getCollaborators()); - } - - function getNumActiveCollaborators() { - - if (!isset($this->ht['active_collaborators'])) - $this->ht['active_collaborators'] = count($this->getActiveCollaborators()); - - return $this->ht['active_collaborators']; - } - - function getActiveCollaborators() { - return $this->getCollaborators(array('isactive'=>1)); - } - - - function getCollaborators($criteria=array()) { - - if ($criteria) - return Collaborator::forThread($this->getThreadId(), $criteria); - - if (!isset($this->collaborators)) - $this->collaborators = Collaborator::forThread($this->getThreadId()); - - return $this->collaborators; - } - //UserList of recipients (owner + collaborators) function getRecipients() { @@ -1836,7 +1807,7 @@ implements RestrictedAccess, Threadable, TemplateVariable { return new FormattedDate($this->getCloseDate()); break; case 'last_update': - return new FormattedDate($upd); + return new FormattedDate($this->last_update); case 'user': return $this->getOwner(); default: @@ -2534,7 +2505,7 @@ implements RestrictedAccess, Threadable, TemplateVariable { } // Threadable interface - function postThreadEntry($type, $vars) { + function postThreadEntry($type, $vars, $options=array()) { $errors = array(); switch ($type) { case 'M': @@ -3123,7 +3094,7 @@ implements RestrictedAccess, Threadable, TemplateVariable { if ($vars['topicId']) { if ($topic=Topic::lookup($vars['topicId'])) { foreach ($topic_forms as $topic_form) { - $TF = $topic_form->getForm()->getForm($vars); + $TF = $topic_form->getForm($vars); if (!$TF->isValid($field_filter('topic'))) $errors = array_merge($errors, $TF->errors()); } diff --git a/include/class.user.php b/include/class.user.php index 960588dae4853e7a8f1952b7c4ac4d3885450104..1b4e309d8af92aae4aef4e277ae23df2c0ffb894 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -685,7 +685,7 @@ implements TemplateVariable { if (!($mails = Mail_RFC822::parseAddressList($this->address)) || PEAR::isError($mails)) return ''; - if (!$list && count($mails) > 1) + if (count($mails) > 1) return ''; $info = $mails[0]; diff --git a/include/client/header.inc.php b/include/client/header.inc.php index db6e4006c2b72d9d8a06036189b3f35b65c6634f..9d39fba5fac3eac0f12d152673b12fc5c8522a5a 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -8,11 +8,17 @@ $signout_url = ROOT_PATH . "logout.php?auth=".$ost->getLinkToken(); header("Content-Type: text/html; charset=UTF-8"); ?> <!DOCTYPE html> -<html <?php +<html<?php if (($lang = Internationalization::getCurrentLanguage()) && ($info = Internationalization::getLanguageInfo($lang)) && (@$info['direction'] == 'rtl')) - echo 'dir="rtl" class="rtl"'; + echo ' dir="rtl" class="rtl"'; +if ($lang) { + $langs = array_unique(array($lang, $cfg->getPrimaryLanguage())); + $langs = Internationalization::rfc1766($langs); + echo ' lang="' . $lang . '"'; + header("Content-Language: ".implode(', ', $langs)); +} ?>> <head> <meta charset="utf-8"> @@ -33,7 +39,7 @@ if (($lang = Internationalization::getCurrentLanguage()) <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/font-awesome.min.css"> <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/flags.css"> <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/rtl.css"/> - <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/chosen.min.css"> + <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/select2.min.css"> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-1.11.2.min.js"></script> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-ui-1.10.3.custom.min.js"></script> <script src="<?php echo ROOT_PATH; ?>js/osticket.js"></script> @@ -43,11 +49,30 @@ if (($lang = Internationalization::getCurrentLanguage()) <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-plugins.js"></script> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-osticket.js"></script> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-fonts.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/chosen.jquery.min.js"></script> + <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/select2.full.min.js"></script> <?php if($ost && ($headers=$ost->getExtraHeaders())) { echo "\n\t".implode("\n\t", $headers)."\n"; } + + // Offer alternate links for search engines + // @see https://support.google.com/webmasters/answer/189077?hl=en + if (($all_langs = Internationalization::getConfiguredSystemLanguages()) + && (count($all_langs) > 1) + ) { + $langs = Internationalization::rfc1766(array_keys($all_langs)); + $qs = array(); + parse_str($_SERVER['QUERY_STRING'], $qs); + foreach ($langs as $L) { + $qs['lang'] = $L; ?> + <link rel="alternate" href="//<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>?<?php + echo http_build_query($qs); ?>" hreflang="<?php echo $L; ?>" /> +<?php + } ?> + <link rel="alternate" href="//<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>" + hreflang="x-default"; +<?php + } ?> </head> <body> diff --git a/include/client/profile.inc.php b/include/client/profile.inc.php index 553005bd994f947cdb7baaeb80c3184558daef61..713551110b2acfdcb8f4cd26b0c65043e3fcf933 100644 --- a/include/client/profile.inc.php +++ b/include/client/profile.inc.php @@ -25,18 +25,14 @@ if ($acct = $thisclient->getAccount()) { <?php echo __('Time Zone');?>: </td> <td> - <select name="timezone" class="chosen-select" id="timezone-dropdown"> - <option value=""><?php echo __('System Default'); ?></option> -<?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($info['timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/', ' / ', $zone); ?></option> -<?php } ?> - </select> + <?php + $TZ_NAME = 'timezone'; + $TZ_TIMEZONE = $info['timezone']; + include INCLUDE_DIR.'staff/templates/timezone.tmpl.php'; ?> <div class="error"><?php echo $errors['timezone']; ?></div> </td> </tr> +<?php if ($cfg->getSecondaryLanguages()) { ?> <tr> <td width="180"> <?php echo __('Preferred Language'); ?>: @@ -55,7 +51,8 @@ $selected = ($info['lang'] == $l['code']) ? 'selected="selected"' : ''; ?> <span class="error"> <?php echo $errors['lang']; ?></span> </td> </tr> -<?php if ($acct->isPasswdResetEnabled()) { ?> +<?php } + if ($acct->isPasswdResetEnabled()) { ?> <tr> <td colspan=2"> <div><hr><h3><?php echo __('Access Credentials'); ?></h3></div> @@ -101,10 +98,3 @@ $selected = ($info['lang'] == $l['code']) ? 'selected="selected"' : ''; ?> window.location.href='index.php';"/> </p> </form> -<script type="text/javascript"> -$('#timezone-dropdown').chosen({ - header: <?php echo JsonDataEncoder::encode(__('Time Zones')); ?>, - allow_single_deselect: true, - width: '350px' -}); -</script> diff --git a/include/client/register.inc.php b/include/client/register.inc.php index 6ac754b8a07b412a43fa1f931c32504337d3f1ef..bf6830952a22b1ca359f5259343784e02edfcec5 100644 --- a/include/client/register.inc.php +++ b/include/client/register.inc.php @@ -40,16 +40,10 @@ $info = Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Time Zone');?>: </td> <td> - <select name="timezone" class="chosen-select" id="timezone-dropdown" - data-placeholder="<?php echo __('System Default'); ?>"> - <option value=""></option> -<?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($info['timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/',' / ',$zone); ?></option> -<?php } ?> - </select> + <?php + $TZ_NAME = 'timezone'; + $TZ_TIMEZONE = $info['timezone']; + include INCLUDE_DIR.'staff/templates/timezone.tmpl.php'; ?> <div class="error"><?php echo $errors['timezone']; ?></div> </td> </tr> @@ -103,19 +97,13 @@ $info = Format::htmlchars(($errors && $_POST)?$_POST:$info); window.location.href='index.php';"/> </p> </form> -<script type="text/javascript"> -$('#timezone-dropdown').chosen({ - allow_single_deselect: true, - width: '350px' -}); -</script> <?php if (!isset($info['timezone'])) { ?> <!-- Auto detect client's timezone where possible --> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jstz.min.js"></script> <script type="text/javascript"> $(function() { var zone = jstz.determine(); - $('#timezone-dropdown').val(zone.name()).trigger('chosen:updated'); + $('#timezone-dropdown').val(zone.name()).trigger('change'); }); </script> <?php } diff --git a/include/client/templates/sidebar.tmpl.php b/include/client/templates/sidebar.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..e3f15419e63894162ccfece5e99c3362910d6fda --- /dev/null +++ b/include/client/templates/sidebar.tmpl.php @@ -0,0 +1,43 @@ +<?php +$BUTTONS = isset($BUTTONS) ? $BUTTONS : true; +?> + <div class="sidebar pull-right"> +<?php if ($BUTTONS) { ?> + <div class="front-page-button flush-right"> +<p> + <a href="open.php" style="display:block" class="blue button"><?php + echo __('Open a New Ticket');?></a> +</p> +<?php if ($cfg && !$cfg->isKnowledgebaseEnabled()) { ?> +<p> + <a href="view.php" style="display:block" class="green button"><?php + echo __('Check Ticket Status');?></a> +</p> +<?php } ?> + </div> +<?php } ?> + <div class="content"><?php + $faqs = FAQ::getFeatured()->select_related('category')->limit(5); + if ($faqs->all()) { ?> + <section><div class="header"><?php echo __('Featured Questions'); ?></div> +<?php foreach ($faqs as $F) { ?> + <div><a href="<?php echo ROOT_PATH; ?>/kb/faq.php?id=<?php + echo urlencode($F->getId()); + ?>"><?php echo $F->getLocalQuestion(); ?></a></div> +<?php } ?> + </section> +<?php + } + $resources = Page::getActivePages()->filter(array('type'=>'other')); + if ($resources->all()) { ?> + <section><div class="header"><?php echo __('Other Resources'); ?></div> +<?php foreach ($resources as $page) { ?> + <div><a href="<?php echo ROOT_PATH; ?>pages/<?php echo $page->getNameAsSlug(); + ?>"><?php echo $page->getLocalName(); ?></a></div> +<?php } ?> + </section> +<?php + } + ?></div> + </div> + diff --git a/include/client/templates/ticket-print.tmpl.php b/include/client/templates/ticket-print.tmpl.php index a1173b217b48111ebc8857129cd9c8e4fe74a56a..d8a4f164316118f40779af197123df8eff9c6026 100644 --- a/include/client/templates/ticket-print.tmpl.php +++ b/include/client/templates/ticket-print.tmpl.php @@ -160,14 +160,10 @@ div.hr { <?php foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) { // Skip core fields shown earlier in the ticket view - // TODO: Rewrite getAnswers() so that one could write - // ->getAnswers()->filter(not(array('field__name__in'=> - // array('email', ...)))); - $answers = array_filter($form->getAnswers(), function ($a) { - return !in_array($a->getField()->get('name'), - array('email','subject','name','priority')) - && !$a->getField()->get('private'); - }); + $answers = $form->getAnswers()->exclude(Q::any(array( + 'field__flags__hasbit' => DynamicFormField::FLAG_EXT_STORED | DynamicFormField::FLAG_CLIENT_VIEW, + 'field__name__in' => array('subject', 'priority'), + ))); if (count($answers) == 0) continue; ?> diff --git a/include/client/view.inc.php b/include/client/view.inc.php index 249f3058bbf7a5981116c86528ca0776ebaf2751..4f96855d7fe0822a3c14c7a27877d9c26bd601b3 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -31,7 +31,8 @@ if ($thisclient && $thisclient->isGuest() <tr> <td colspan="2" width="100%"> <h1> - <?php echo sprintf(__('Ticket #%s'), $ticket->getNumber()); ?> + <b><?php echo $ticket->getSubject(); ?></b> + <small>#<?php echo $ticket->getNumber(); ?></small> <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="<?php echo __('Reload'); ?>"><span class="Icon refresh"> </span></a> <div class="pull-right"> <a class="action-button" href="tickets.php?a=print&id=<?php @@ -39,7 +40,7 @@ if ($thisclient && $thisclient->isGuest() <?php if ($ticket->hasClientEditableFields() // Only ticket owners can edit the ticket details (and other forms) && $thisclient->getId() == $ticket->getUserId()) { ?> - <a class="action-button pull-right" href="tickets.php?a=edit&id=<?php + <a class="action-button" href="tickets.php?a=edit&id=<?php echo $ticket->getId(); ?>"><i class="icon-edit"></i> <?php echo __('Edit'); ?></a> <?php } ?> </div> @@ -49,6 +50,11 @@ if ($thisclient && $thisclient->isGuest() <tr> <td width="50%"> <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0"> + <thead> + <tr><td class="headline" colspan="2"> + <?php echo __('Basic Ticket Information'); ?> + </td></tr> + </thead> <tr> <th width="100"><?php echo __('Ticket Status');?>:</th> <td><?php echo ($S = $ticket->getStatus()) ? $S->getLocalName() : ''; ?></td> @@ -65,6 +71,11 @@ if ($thisclient && $thisclient->isGuest() </td> <td width="50%"> <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0"> + <thead> + <tr><td class="headline" colspan="2"> + <?php echo __('User Information'); ?> + </td></tr> + </thead> <tr> <th width="100"><?php echo __('Name');?>:</th> <td><?php echo mb_convert_case(Format::htmlchars($ticket->getName()), MB_CASE_TITLE); ?></td> @@ -81,32 +92,40 @@ if ($thisclient && $thisclient->isGuest() </td> </tr> <tr> + <td colspan="2"> +<!-- Custom Data --> <?php -foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) { - $answers = $form->getAnswers(); - if ($idx > 0 and $idx % 2 == 0) { ?> - </tr><tr> - <?php } ?> - <td width="50%"> - <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0"> - <?php foreach ($answers as $answer) { - if (in_array($answer->getField()->get('name'), array('name', 'email', 'subject'))) - continue; - elseif (!$answer->getField()->isVisibleToUsers()) - continue; - ?> - <tr> - <th width="100"><?php echo $answer->getField()->get('label'); - ?>:</th> - <td><?php echo $answer->display(); ?></td> - </tr> - <?php } ?> - </table></td> -<?php } ?> +foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) { + // Skip core fields shown earlier in the ticket view + $answers = $form->getAnswers()->exclude(Q::any(array( + 'field__flags__hasbit' => DynamicFormField::FLAG_EXT_STORED, + 'field__name__in' => array('subject', 'priority'), + Q::not(array('field__flags__hasbit' => DynamicFormField::FLAG_CLIENT_VIEW)), + ))); + if (count($answers) == 0) + continue; + ?> + <table class="custom-data" cellspacing="0" cellpadding="4" width="100%" border="0"> + <tr><td colspan="2" class="headline flush-left"><?php echo $form->getTitle(); ?></th></tr> + <?php foreach($answers as $a) { + if (!($v = $a->display())) continue; ?> + <tr> + <th><?php + echo $a->getField()->get('label'); + ?>:</th> + <td><?php + echo $v; + ?></td> + </tr> + <?php } ?> + </table> + <?php + $idx++; +} ?> + </td> </tr> </table> <br> -<div class="subject"><?php echo __('Subject'); ?>: <strong><?php echo Format::htmlchars($ticket->getSubject()); ?></strong></div> <div id="ticketThread"> <?php if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) { @@ -128,16 +147,27 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) { </th></tr> <tr><td class="thread-body"><div><?php echo Format::clickableurls($entry->getBody()->toHtml()); ?></div></td></tr> <?php - if($entry->has_attachments - && ($urls = $entry->getAttachmentUrls()) - && ($links = $entry->getAttachmentsLinks())) { ?> - <tr><td class="info"><?php echo $links; ?></td></tr> -<?php } - if ($urls) { ?> - <script type="text/javascript"> - $(function() { showImagesInline(<?php echo - JsonDataEncoder::encode($urls); ?>); }); - </script> + $urls = null; + if ($entry->has_attachments + && ($urls = $entry->getAttachmentUrls())) { ?> + <tr> + <td class="info"><?php + foreach ($entry->attachments as $A) { + if ($A->inline) continue; + $size = ''; + if ($A->file->size) + $size = sprintf('<em>(%s)</em>', + Format::file_size($A->file->size)); +?> + <i class="icon-paperclip"></i> + <a class="no-pjax" href="<?php echo $A->file->getDownloadUrl(); + ?>" download="<?php echo Format::htmlchars($A->file->name); ?>" + target="_blank"> + <?php echo Format::htmlchars($A->file->name); + ?></a><?php echo $size;?> +<?php } ?> + </td> + </tr> <?php } ?> </table> <?php @@ -152,46 +182,37 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) { <div id="msg_notice"><?php echo $msg; ?></div> <?php }elseif($warn) { ?> <div id="msg_warning"><?php echo $warn; ?></div> -<?php } ?> - -<?php +<?php } if (!$ticket->isClosed() || $ticket->isReopenable()) { ?> -<form id="reply" action="tickets.php?id=<?php echo $ticket->getId(); ?>#reply" name="reply" method="post" enctype="multipart/form-data"> +<form id="reply" action="tickets.php?id=<?php echo $ticket->getId(); +?>#reply" name="reply" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <h2><?php echo __('Post a Reply');?></h2> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> <input type="hidden" name="a" value="reply"> - <table border="0" cellspacing="0" cellpadding="3" style="width:100%"> - <tr> - <td colspan="2"> - <?php - if($ticket->isClosed()) { - $msg='<b>'.__('Ticket will be reopened on message post').'</b>'; - } else { - $msg=__('To best assist you, we request that you be specific and detailed'); - } - ?> - <span id="msg"><em><?php echo $msg; ?> </em></span><font class="error">* <?php echo $errors['message']; ?></font> - <br/> - <textarea name="message" id="message" cols="50" rows="9" wrap="soft" - class="<?php if ($cfg->isHtmlThreadEnabled()) echo 'richtext'; - ?> draft" <?php - list($draft, $attrs) = Draft::getDraftAndDataAttrs('ticket.client', $ticket->getId(), $info['message']); - echo $attrs; ?>><?php echo $draft ?: $info['message']; - ?></textarea> - <?php - if ($messageField->isAttachmentsEnabled()) { ?> -<?php - print $attachments->render(array('client'=>true)); - print $attachments->getForm()->getMedia(); -?> - <?php - } ?> - </td> - </tr> - </table> - <p style="padding-left:165px;"> + <div> + <p><em><?php + echo __('To best assist you, we request that you be specific and detailed'); ?></em> + <font class="error">* <?php echo $errors['message']; ?></font> + </p> + <textarea name="message" id="message" cols="50" rows="9" wrap="soft" + class="<?php if ($cfg->isHtmlThreadEnabled()) echo 'richtext'; + ?> draft" <?php +list($draft, $attrs) = Draft::getDraftAndDataAttrs('ticket.client', $ticket->getId(), $info['message']); +echo $attrs; ?>><?php echo $draft ?: $info['message']; + ?></textarea> + <?php + if ($messageField->isAttachmentsEnabled()) { + print $attachments->render(array('client'=>true)); + } ?> + </div> +<?php if ($ticket->isClosed()) { ?> + <div class="warning-banner"> + <?php echo __('Ticket will be reopened on message post'); ?> + </div> +<?php } ?> + <p style="text-align:center"> <input type="submit" value="<?php echo __('Post Reply');?>"> <input type="reset" value="<?php echo __('Reset');?>"> <input type="button" value="<?php echo __('Cancel');?>" onClick="history.go(-1)"> @@ -199,3 +220,18 @@ if (!$ticket->isClosed() || $ticket->isReopenable()) { ?> </form> <?php } ?> +<script type="text/javascript"> +<?php +// Hover support for all inline images +$urls = array(); +foreach (AttachmentFile::objects()->filter(array( + 'attachments__thread_entry__thread__id' => $ticket->getThreadId(), + 'attachments__inline' => true, +)) as $file) { + $urls[strtolower($file->getKey())] = array( + 'download_url' => $file->getDownloadUrl(), + 'filename' => $file->name, + ); +} ?> +showImagesInline(<?php echo JsonDataEncoder::encode($urls); ?>); +</script> diff --git a/include/i18n/en_US/config.yaml b/include/i18n/en_US/config.yaml index 35e5a830b4273a84e875c74a3fa92912ed40178e..c849302f40086e18fa46a4bf47143faedf075622 100644 --- a/include/i18n/en_US/config.yaml +++ b/include/i18n/en_US/config.yaml @@ -4,10 +4,10 @@ # --- core: - time_format: 'h:i A' - date_format: 'm/d/Y' - datetime_format: 'm/d/Y g:i a' - daydatetime_format: 'D, M j Y g:ia' + time_format: 'hh:mm a' + date_format: 'MM/dd/y' + datetime_format: 'MM/dd/y h:mm a' + daydatetime_format: 'EEE, MMM d y h:mm a' default_priority_id: 2 enable_daylight_saving: 0 diff --git a/include/i18n/en_US/file.yaml b/include/i18n/en_US/file.yaml index 0464b6a23d6f2e600b2242880089133d65fdc214..d77bcd3e9da33e29462550b07c4c2efabca5f4c4 100644 --- a/include/i18n/en_US/file.yaml +++ b/include/i18n/en_US/file.yaml @@ -3,7 +3,7 @@ # # Files initially inserted into the system. Canned responses have their own # method for attachments; however, this file will make it easier to add -# thinkgs like inline images. +# things like inline images. # # NOTE: If the files aren't attached to something by the installer, they # bill be cleaned up shortly after installation (by the autocron). diff --git a/include/i18n/en_US/help_topic.yaml b/include/i18n/en_US/help_topic.yaml index d54cdf3d702e391fea43737d8d455595a00eedf1..ab9c195eb21cf4b828f1d37f4d30c02a782eae84 100644 --- a/include/i18n/en_US/help_topic.yaml +++ b/include/i18n/en_US/help_topic.yaml @@ -23,6 +23,7 @@ ispublic: 1 dept_id: 1 priority_id: 2 + forms: [2] topic: General Inquiry notes: | Questions about products or services @@ -31,6 +32,7 @@ ispublic: 1 dept_id: 1 priority_id: 1 + forms: [2] topic: Feedback notes: | Tickets that primarily concern the sales and billing departments @@ -40,6 +42,7 @@ ispublic: 1 dept_id: 1 priority_id: 2 + forms: [2] topic: Report a Problem notes: | Product, service, or equipment related issues @@ -50,6 +53,7 @@ dept_id: 1 sla_id: 1 priority_id: 3 + forms: [2] topic: Access Issue notes: | Report an inability access a physical or virtual asset diff --git a/include/i18n/en_US/role.yaml b/include/i18n/en_US/role.yaml index 4e3a15e839dc06621146f12a3ca46e1f24ed2e3f..ec55b154de8ee10dd0e10c977f323439c5132466 100644 --- a/include/i18n/en_US/role.yaml +++ b/include/i18n/en_US/role.yaml @@ -32,9 +32,10 @@ task.close, task.delete, kb.premade, - kb.faq, + faq.manage, stats.agents, emails.banlist, + user.create, user.edit, user.delete, user.manage, @@ -64,9 +65,10 @@ task.reply, task.close, kb.premade, - kb.faq, + faq.manage, stats.agents, emails.banlist, + user.create, user.edit, user.delete, user.manage, diff --git a/include/staff/apikey.inc.php b/include/staff/apikey.inc.php index fdf30da4bc844ed0cbdef37c3902a3916eca2716..5096c5150de3f59caf061b2830802909aca0460a 100644 --- a/include/staff/apikey.inc.php +++ b/include/staff/apikey.inc.php @@ -70,7 +70,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <span> - <input type="text" size="30" name="ipaddr" value="<?php echo $info['ipaddr']; ?>"> + <input type="text" size="30" name="ipaddr" value="<?php echo $info['ipaddr']; ?>"i + autofocus> <span class="error">* <?php echo $errors['ipaddr']; ?></span> <i class="help-tip icon-question-sign" href="#ip_addr"></i> </span> diff --git a/include/staff/banlist.inc.php b/include/staff/banlist.inc.php index 3fd33ed5a98840bf6136ed1b10b9c06f96ffa6b3..4093439573edb4df326db69e7b5c9763c4a63944 100644 --- a/include/staff/banlist.inc.php +++ b/include/staff/banlist.inc.php @@ -55,8 +55,9 @@ $query="$select $from $where ORDER BY $order_by LIMIT ".$pageNav->getStart()."," <form action="banlist.php" method="GET" name="filter"> <input type="hidden" name="a" value="filter" > <div> - <?php echo __('Query');?>: <input name="q" type="text" size="20" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> - + <?php echo __('Query');?>: + <input name="q" type="search" size="30" autofocus + value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> <input type="submit" name="submit" value="<?php echo __('Search');?>"/> </div> </form> diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php index db23768ef4cfc9b2e8020fa34241c7777647e0fd..826c2ab2eee407c314b3bd39aedfa12d111649b4 100644 --- a/include/staff/department.inc.php +++ b/include/staff/department.inc.php @@ -32,7 +32,7 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2><?php echo __('Department');?></h2> <br> -<ul class="tabs"> +<ul class="clean tabs"> <li class="active"><a href="#settings"> <i class="icon-file"></i> <?php echo __('Settings'); ?></a></li> <li><a href="#access"> @@ -72,7 +72,8 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); </td> <td> <input data-translate-tag="<?php echo $dept ? $dept->getTranslateTag() : ''; - ?>" type="text" size="30" name="name" value="<?php echo $info['name']; ?>"> + ?>" type="text" size="30" name="name" value="<?php echo $info['name']; ?>" + autofocus> <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php index e194d4d8561b4d0a35fb365a85e8c7ca356ca6d3..ed2ec1e4cf53b9281ec75c12883c6f1ae5bf8a00 100644 --- a/include/staff/dynamic-form.inc.php +++ b/include/staff/dynamic-form.inc.php @@ -15,7 +15,7 @@ if($form && $_REQUEST['a']!='add') { $translations = CustomDataTranslation::allTranslations($trans, 'phrase'); $_keys = array_flip($trans); foreach ($translations as $t) { - if (!Internationalization::isLanguageInstalled($t->lang)) + if (!Internationalization::isLanguageEnabled($t->lang)) continue; // Create keys of [trans][de_DE][title] for instance $info['trans'][$t->lang][$_keys[$t->object_hash]] @@ -73,7 +73,7 @@ if ($form && count($langs) > 1) { ?> lang="<?php echo $cfg->getPrimaryLanguage(); ?>"> <div class="required"><?php echo __('Title'); ?>:</div> <div> - <input type="text" name="title" size="60" + <input type="text" name="title" size="60" autofocus value="<?php echo $info['title']; ?>"/> <i class="help-tip icon-question-sign" href="#form_title"></i> <div class="error"><?php @@ -289,7 +289,7 @@ if ($form && count($langs) > 1) { ?> </p> <div style="display:none;" class="draggable dialog" id="delete-confirm"> - <h3><i class="icon-trash"></i> <?php echo __('Remove Existing Data?'); ?></h3> + <h3 class="drag-handle"><i class="icon-trash"></i> <?php echo __('Remove Existing Data?'); ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> <p> diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php index ed7030ff611136393d2e5b5889d5aba1c63c8788..3755f2e38b8e030b051c34c4fd8b0fbe3a6fc246 100644 --- a/include/staff/dynamic-list.inc.php +++ b/include/staff/dynamic-list.inc.php @@ -27,7 +27,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) <h2><?php echo __('Custom List'); ?> <?php echo $list ? $list->getName() : __('Add new list'); ?></h2> -<ul class="tabs" id="list-tabs"> +<ul class="clean tabs" id="list-tabs"> <li class="active"><a href="#definition"> <i class="icon-plus"></i> <?php echo __('Definition'); ?></a></li> <li><a href="#items"> @@ -57,7 +57,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) echo $list->getName(); else { echo sprintf('<input size="50" type="text" name="name" - data-translate-tag="%s" + data-translate-tag="%s" autofocus value="%s"/> <span class="error">*<br/>%s</span>', $trans['name'], $info['name'], $errors['name']); diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php index 8958d43a0fcb022d51445ebe93d2d4a665638a9d..5cc183baab6d05f5ce14e60d7d8feb6ed5219c39 100644 --- a/include/staff/email.inc.php +++ b/include/staff/email.inc.php @@ -55,7 +55,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Email Address');?> </td> <td> - <input type="text" size="35" name="email" value="<?php echo $info['email']; ?>"> + <input type="text" size="35" name="email" value="<?php echo $info['email']; ?>" + autofocus> <span class="error">* <?php echo $errors['email']; ?></span> </td> </tr> diff --git a/include/staff/faq-categories.inc.php b/include/staff/faq-categories.inc.php index d66c24470c1475e5d3b05697f1d23ae1ebcfb796..52d4f8d49cdc81e7eaaf92e3590a473da3b2d14a 100644 --- a/include/staff/faq-categories.inc.php +++ b/include/staff/faq-categories.inc.php @@ -6,7 +6,8 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied'); <form id="kbSearch" action="kb.php" method="get"> <input type="hidden" name="a" value="search"> <div> - <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> + <input id="query" type="search" size="20" name="q" autofocus + value="<?php echo Format::htmlchars($_REQUEST['q']); ?>"> <select name="cid" id="cid"> <option value="">— <?php echo __('All Categories');?> —</option> <?php diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php index cb6ffed91153a90876b202ca5d9a55bfaababfb3..9fcde9571c961625907edebd59e4c8e2f5b6e1e6 100644 --- a/include/staff/faq.inc.php +++ b/include/staff/faq.inc.php @@ -89,7 +89,7 @@ if ($topics = Topic::getAllHelpTopics()) { <?php } ?> </select> <script type="text/javascript"> - $(function() { $("#help-topic-selection").chosen(); }); + $(function() { $("#help-topic-selection").select2(); }); </script> <?php } ?> </div> diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php index 3c0f8f49ada7acf811697b500ee1fd104b447b1f..144d0321a3e572ffca09003921c5cef8fce4f0a1 100644 --- a/include/staff/filter.inc.php +++ b/include/staff/filter.inc.php @@ -42,7 +42,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Filter Name');?>: </td> <td> - <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>"> + <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>" + autofocus> <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> diff --git a/include/staff/footer.inc.php b/include/staff/footer.inc.php index 141a859dd530c066a5e3403d2f0e75955a96bc6f..ec372c7c7b364c487d038b30dabf24d743cab578 100644 --- a/include/staff/footer.inc.php +++ b/include/staff/footer.inc.php @@ -40,16 +40,26 @@ if(is_object($thisstaff) && $thisstaff->isStaff()) { ?> <div class="clear"></div> </div> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery.pjax.js"></script> +<script type="text/javascript" src="./js/scp.js"></script> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-ui-1.10.3.custom.min.js"></script> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/filedrop.field.js"></script> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/select2.full.min.js"></script> +<script type="text/javascript" src="./js/tips.js"></script> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor.min.js"></script> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-osticket.js"></script> +<script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-plugins.js"></script> +<script type="text/javascript" src="./js/bootstrap-typeahead.js"></script> +<script type="text/javascript" src="./js/jquery.translatable.js"></script> +<script type="text/javascript" src="./js/jquery.dropdown.js"></script> +<script type="text/javascript" src="./js/bootstrap-tooltip.js"></script> +<link type="text/css" rel="stylesheet" href="./css/tooltip.css"> <script type="text/javascript"> -if ($.support.pjax) { - $(document).on('click', 'a', function(event) { - var $this = $(this); - if (!$this.hasClass('no-pjax') - && !$this.closest('.no-pjax').length - && $this.attr('href')[0] != '#') - $.pjax.click(event, {container: $this.data('pjaxContainer') || $('#pjax-container'), timeout: 2000}); - }) -} + getConfig().resolve(<?php + include INCLUDE_DIR . 'ajax.config.php'; + $api = new ConfigAjaxAPI(); + print $api->scp(false); + ?>); </script> <?php if ($thisstaff diff --git a/include/staff/group.inc.php b/include/staff/group.inc.php index e8006cade91f1a065cbd54b3a0c1ec9d5849b18e..d13205385c286281d85558dc028d42571b1ce781 100644 --- a/include/staff/group.inc.php +++ b/include/staff/group.inc.php @@ -30,7 +30,7 @@ $roles = Role::getActiveRoles(); <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2> <?php echo $group ?: __('New Group'); ?></h2> <br> -<ul class="tabs"> +<ul class="clean tabs"> <li class="active"><a href="#group"> <i class="icon-file"></i> <?php echo __('Group'); ?></a></li> <li><a href="#departments"> @@ -54,7 +54,7 @@ $roles = Role::getActiveRoles(); <td width="180" class="required"><?php echo __('Name'); ?>:</td> <td> <input size="50" type="text" name="name" value="<?php echo $info['name']; ?>" - data-translate-tag="<?php echo $trans['name']; ?>"/> + autofocus data-translate-tag="<?php echo $trans['name']; ?>"/> <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> diff --git a/include/staff/header.inc.php b/include/staff/header.inc.php index 0945a916441d42a0b97f7034ced1bca34036983f..8ceef837a5488e15199e1d04402ae495ffb94b5f 100644 --- a/include/staff/header.inc.php +++ b/include/staff/header.inc.php @@ -2,11 +2,14 @@ header("Content-Type: text/html; charset=UTF-8"); if (!isset($_SERVER['HTTP_X_PJAX'])) { ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> -<html <?php +<html<?php if (($lang = Internationalization::getCurrentLanguage()) && ($info = Internationalization::getLanguageInfo($lang)) && (@$info['direction'] == 'rtl')) - echo 'dir="rtl" class="rtl"'; + echo ' dir="rtl" class="rtl"'; +if ($lang) { + echo ' lang="' . Internationalization::rfc1766($lang) . '"'; +} ?>> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> @@ -21,17 +24,6 @@ if (($lang = Internationalization::getCurrentLanguage()) </style> <![endif]--> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-1.11.2.min.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-ui-1.10.3.custom.min.js"></script> - <script type="text/javascript" src="./js/scp.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery.pjax.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/filedrop.field.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/chosen.jquery.min.js"></script> - <script type="text/javascript" src="./js/tips.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor.min.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-osticket.js"></script> - <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-plugins.js"></script> - <script type="text/javascript" src="./js/bootstrap-typeahead.js"></script> - <script type="text/javascript" src="./js/jquery.translatable.js"></script> <link rel="stylesheet" href="<?php echo ROOT_PATH ?>css/thread.css" media="all"> <link rel="stylesheet" href="./css/scp.css" media="all"> <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/redactor.css" media="screen"> @@ -45,10 +37,9 @@ if (($lang = Internationalization::getCurrentLanguage()) <link type="text/css" rel="stylesheet" href="./css/dropdown.css"> <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/loadingbar.css"/> <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/flags.css"> - <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/chosen.min.css"> + <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/select2.min.css"> <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/rtl.css"/> <link type="text/css" rel="stylesheet" href="./css/translatable.css"/> - <script type="text/javascript" src="./js/jquery.dropdown.js"></script> <?php if($ost && ($headers=$ost->getExtraHeaders())) { diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index 7df72585c1efdc152630af9438bf53a09419106d..f6dbcfe7bb813d619ccbf60043e9ab2a940457fb 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -32,7 +32,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <br/> -<ul class="tabs" id="topic-tabs"> +<ul class="clean tabs" id="topic-tabs"> <li class="active"><a href="#info"><i class="icon-info-sign"></i> Help Topic Information</a></li> <li><a href="#routing"><i class="icon-ticket"></i> New ticket options</a></li> <li><a href="#forms"><i class="icon-paste"></i> Forms</a></li> @@ -54,7 +54,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <input type="text" size="30" name="topic" value="<?php echo $info['topic']; ?>" - data-translate-tag="<?php echo $trans['name']; ?>"/> + autofocus data-translate-tag="<?php echo $trans['name']; ?>"/> <span class="error">* <?php echo $errors['topic']; ?></span> <i class="help-tip icon-question-sign" href="#topic"></i> </td> </tr> @@ -370,7 +370,7 @@ foreach ($forms as $F) { <th><?php echo __('Variable'); ?></th> </tr> <?php - foreach ($F->getFields() as $f) { ?> + foreach ($F->getDynamicFields() as $f) { ?> <tr> <td><input type="checkbox" name="fields[]" value="<?php echo $f->get('id'); ?>" <?php @@ -440,21 +440,20 @@ $(function() { } }); }); - + $('table#topic-forms').sortable({ + items: 'tbody', + handle: 'td.handle', + tolerance: 'pointer', + forcePlaceholderSize: true, + helper: function(e, ui) { + ui.children().each(function() { + $(this).children().each(function() { + $(this).width($(this).width()); + }); + }); + ui=ui.clone().css({'background-color':'white', 'opacity':0.8}); + return ui; + } + }).disableSelection(); }); -$('table#topic-forms').sortable({ - items: 'tbody', - handle: 'td.handle', - tolerance: 'pointer', - forcePlaceholderSize: true, - helper: function(e, ui) { - ui.children().each(function() { - $(this).children().each(function() { - $(this).width($(this).width()); - }); - }); - ui=ui.clone().css({'background-color':'white', 'opacity':0.8}); - return ui; - } -}).disableSelection(); </script> diff --git a/include/staff/login.tpl.php b/include/staff/login.tpl.php index cbb5b589499552516ea20f60b8bd90917ff2d445..296b76af92e4ba5b9d00793e62fb53190d8168e8 100644 --- a/include/staff/login.tpl.php +++ b/include/staff/login.tpl.php @@ -13,7 +13,9 @@ $info = ($_POST && $errors)?Format::htmlchars($_POST):array(); <?php csrf_token(); ?> <input type="hidden" name="do" value="scplogin"> <fieldset> - <input type="text" name="userid" id="name" value="<?php echo $info['userid']; ?>" placeholder="<?php echo __('Email or Username'); ?>" autocorrect="off" autocapitalize="off"> + <input type="text" name="userid" id="name" value="<?php + echo $info['userid']; ?>" placeholder="<?php echo __('Email or Username'); ?>" + autofocus autocorrect="off" autocapitalize="off"> <input type="password" name="passwd" id="pass" placeholder="<?php echo __('Password'); ?>" autocorrect="off" autocapitalize="off"> <?php if ($show_reset && $cfg->allowPasswordReset()) { ?> <h3 style="display:inline"><a href="pwreset.php"><?php echo __('Forgot my password'); ?></a></h3> diff --git a/include/staff/org-view.inc.php b/include/staff/org-view.inc.php index 492a5b86c51a9725f0a4a34de459afe66aa8cd7f..94e658d24c39cb02253dfb2d2538be5d6f369421 100644 --- a/include/staff/org-view.inc.php +++ b/include/staff/org-view.inc.php @@ -75,7 +75,7 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path') </table> <br> <div class="clear"></div> -<ul class="tabs" id="orgtabs"> +<ul class="clean tabs" id="orgtabs"> <li class="active"><a href="#users"><i class="icon-user"></i> <?php echo __('Users'); ?></a></li> <li><a href="#tickets"><i diff --git a/include/staff/orgs.inc.php b/include/staff/orgs.inc.php index 84288aeda3ad643af8183b1e1a5a37c84ca4ccf6..bc41711ff8b59016163c65248071aef835107011 100644 --- a/include/staff/orgs.inc.php +++ b/include/staff/orgs.inc.php @@ -78,8 +78,9 @@ $_SESSION['orgs_qs_'.$qhash] = $query; <input type="hidden" name="a" value="search"> <table> <tr> - <td><input type="text" id="basic-org-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>" - autocomplete="off" autocorrect="off" autocapitalize="off"></td> + <td><input type="search" id="basic-org-search" name="query" + autofocus size="30" value="<?php echo Format::htmlchars($_REQUEST['query']); ?>" + autocomplete="off" autocorrect="off" autocapitalize="off"></td> <td><input type="submit" name="basic_search" class="button" value="<?php echo __('Search'); ?>"></td> <!-- <td> <a href="" id="advanced-user-search">[advanced]</a></td> --> </tr> diff --git a/include/staff/page.inc.php b/include/staff/page.inc.php index ea373533c886b1b9bd7d11f8cd651e461dca1072..1a959a4c8a3c50f0f03efc507db103266b641add 100644 --- a/include/staff/page.inc.php +++ b/include/staff/page.inc.php @@ -62,7 +62,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <input type="text" size="40" name="name" value="<?php echo $info['name']; ?>" - data-translate-tag="<?php echo $trans['name']; ?>"/> + autofocus data-translate-tag="<?php echo $trans['name']; ?>"/> <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> @@ -111,7 +111,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> <tr> <td colspan="2"> - <ul class="tabs"> + <ul class="tabs clean"> <li class="active"><a href="#content"><?php echo __('Page Content'); ?></a></li> <li><a href="#notes"><?php echo __('Internal Notes'); ?></a></li> </ul> @@ -131,7 +131,14 @@ if ($page && count($langs) > 1) { ?> <?php } ?> </ul> <?php -} ?> +} + +// For landing page, constrain to the diplayed width of 565px; +if ($info['type'] == 'landing') + $width = '565px'; +else + $width = '100%'; +?> </td> <td id="translations_container" style="padding-left: 10px"> <div id="msg_info"> @@ -143,7 +150,8 @@ if ($page && count($langs) > 1) { ?> <div id="translation-<?php echo $cfg->getPrimaryLanguage(); ?>" class="tab_content" lang="<?php echo $cfg->getPrimaryLanguage(); ?>"> - <textarea name="body" cols="21" rows="12" style="width:100%" class="richtext draft" + <textarea name="body" cols="21" rows="12" class="richtext draft" + data-width="<?php echo $width; ?>" <?php if (!$info['type'] || $info['type'] == 'thank-you') echo 'data-root-context="thank-you"'; list($draft, $attrs) = Draft::getDraftAndDataAttrs('page', $info['id'], $info['body']); @@ -154,11 +162,11 @@ if ($page && count($langs) > 1) { ?> foreach ($langs as $tag=>$nfo) { if ($tag == $cfg->getPrimaryLanguage()) continue; ?> - <div id="translation-<?php echo $tag; ?>" class="tab_content" - style="display:none;" lang="<?php echo $tag; ?>"> + <div id="translation-<?php echo $tag; ?>" class="tab_content hidden" + lang="<?php echo $tag; ?>"> <textarea name="trans[<?php echo $tag; ?>][body]" cols="21" rows="12" <?php if ($info['type'] == 'thank-you') echo 'data-root-context="thank-you"'; ?> - style="width:100%" class="richtext draft" + style="width:100%" class="richtext draft" data-width="<?php echo $width; ?>" <?php list($draft, $attrs) = Draft::getDraftAndDataAttrs('page', $info['id'].'.'.$tag, $info['trans'][$tag]); echo $attrs; ?>><?php echo $draft ?: $info['trans'][$tag]; ?></textarea> diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php index 5ef77f8707d4573e7aca06e4e22cefcfef5909d7..a8ba88a3ecce4a8a2aece8139c10ba1774c67104 100644 --- a/include/staff/profile.inc.php +++ b/include/staff/profile.inc.php @@ -85,31 +85,14 @@ $info['id']=$staff->getId(); <?php echo __('Time Zone');?>: </td> <td> - <select name="timezone" class="chosen-select" id="timezone-dropdown" - data-placeholder="<?php echo __('System Default'); ?>"> - <option value=""></option> -<?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($info['timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/',' / ',$zone); ?></option> -<?php } ?> - </select> - <button class="action-button" onclick="javascript: - $('head').append($('<script>').attr('src', '<?php - echo ROOT_PATH; ?>js/jstz.min.js')); - var recheck = setInterval(function() { - if (window.jstz !== undefined) { - clearInterval(recheck); - var zone = jstz.determine(); - $('#timezone-dropdown').val(zone.name()).trigger('chosen:updated'); - - } - }, 200); - return false;"><i class="icon-map-marker"></i> <?php echo __('Auto Detect'); ?></button> + <?php + $TZ_NAME = 'timezone'; + $TZ_TIMEZONE = $info['timezone']; + include STAFFINC_DIR.'templates/timezone.tmpl.php'; ?> <div class="error"><?php echo $errors['timezone']; ?></div> </td> </tr> +<?php if ($cfg->getSecondaryLanguages()) { ?> <tr> <td width="180"> <?php echo __('Preferred Language'); ?>: @@ -128,6 +111,7 @@ $info['id']=$staff->getId(); <span class="error"> <?php echo $errors['lang']; ?></span> </td> </tr> +<?php } ?> <tr><td width="220"><?php echo __('Preferred Locale');?>:</td> <td> <select name="locale"> @@ -273,11 +257,3 @@ $info['id']=$staff->getId(); <input type="button" name="cancel" value="<?php echo __('Cancel Changes');?>" onclick='window.location.href="index.php"'> </p> </form> -<script type="text/javascript"> -!(function() { - $('#timezone-dropdown').chosen({ - allow_single_deselect: true, - width: '350px' - }); -})(); -</script> diff --git a/include/staff/role.inc.php b/include/staff/role.inc.php index 309d26d7bd86dfb74c6deb1fc8ffd020a0a3a92b..7730ea551f005c0f70dd72f021173b17ddcada52 100644 --- a/include/staff/role.inc.php +++ b/include/staff/role.inc.php @@ -25,7 +25,7 @@ $info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $in <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2> <?php echo $role ?: __('New Role'); ?></h2> <br> -<ul class="tabs"> +<ul class="clean tabs"> <li class="active"><a href="#definition"> <i class="icon-file"></i> <?php echo __('Definition'); ?></a></li> <li><a href="#permissions"> @@ -49,7 +49,8 @@ $info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $in <td width="180" class="required"><?php echo __('Name'); ?>:</td> <td> <input size="50" type="text" name="name" value="<?php echo - $info['name']; ?>" data-translate-tag="<?php echo $trans['name']; ?>"/> + $info['name']; ?>" data-translate-tag="<?php echo $trans['name']; ?>" + autofocus/> <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> diff --git a/include/staff/settings-access.inc.php b/include/staff/settings-access.inc.php index 5efac16cf87fe95a258cd6fd3b181d31856507eb..c7cbc5034e79f4347a99778595c6c20f3a2cbc4c 100644 --- a/include/staff/settings-access.inc.php +++ b/include/staff/settings-access.inc.php @@ -168,14 +168,13 @@ $manage_content = function($title, $content) use ($contents) { $notes = explode('. ', $notes); $notes = $notes[0]; ?><tr><td colspan="2"> - <div style="display:inline-block;margin:0 36px"> - <i class="icon-file-text pull-left icon-2x" style="color:#bbb;margin:0 -36px"></i> + <div style="padding:2px 5px"> <a href="#ajax.php/content/<?php echo $id; ?>/manage" onclick="javascript: $.dialog($(this).attr('href').substr(1), 201); return false;" class="pull-left"><i class="icon-file-text icon-2x" style="color:#bbb;"></i> </a> - <span style="display:inline-block;width:90%;padding-left:10px;line-height:1.2em"> + <span style="display:inline-block;width:90%;width:calc(100% - 32px);padding-left:10px;line-height:1.2em"> <a href="#ajax.php/content/<?php echo $id; ?>/manage" onclick="javascript: $.dialog($(this).attr('href').substr(1), 201); diff --git a/include/staff/settings-alerts.inc.php b/include/staff/settings-alerts.inc.php index 3a9326bc4127343b57831a14a81b8d50f1b5c5bb..03168d557c872843ce65ae83c41daa2d8fb12c34 100644 --- a/include/staff/settings-alerts.inc.php +++ b/include/staff/settings-alerts.inc.php @@ -1,8 +1,3 @@ -<h2><?php echo __('Alerts and Notices'); ?> - <i class="help-tip icon-question-sign" href="#page_title"></i></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> @@ -233,8 +228,3 @@ </tr> </tbody> </table> -<p style="text-align:center;"> - <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>"> - <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>"> -</p> -</form> diff --git a/include/staff/settings-autoresp.inc.php b/include/staff/settings-autoresp.inc.php index 0a59f4b9d215589b6f1cd2ad2540854dc4cb4c7c..42a52de40eee2b35f5836ef7639aae04e7b63f5e 100644 --- a/include/staff/settings-autoresp.inc.php +++ b/include/staff/settings-autoresp.inc.php @@ -1,7 +1,3 @@ -<h2><?php echo __('Autoresponder Settings'); ?></h2> -<form action="settings.php?t=autoresp" method="post" id="save"> -<?php csrf_token(); ?> -<input type="hidden" name="t" value="autoresp" > <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> @@ -60,8 +56,3 @@ echo $config['overlimit_notice_active'] ? 'checked="checked"' : ''; ?>/> </tr> </tbody> </table> -<p style="padding-left:200px;"> - <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>"> - <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>"> -</p> -</form> diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php index 9d6e325c41ff0f3e7a1725ecb749ef0191177448..e9a261a213de2326fa45359c5f204f9387956d35 100644 --- a/include/staff/settings-system.inc.php +++ b/include/staff/settings-system.inc.php @@ -145,29 +145,12 @@ $gmtime = Misc::gmtime(); </tr> <tr><td width="220" class="required"><?php echo __('Default Time Zone');?>:</td> <td> - <select name="default_timezone" id="timezone-dropdown"> <?php - foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($config['default_timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/',' / ',$zone); ?></option> - - <?php - } ?> - </select> - <button class="action-button" onclick="javascript: - $('head').append($('<script>').attr('src', '<?php - echo ROOT_PATH; ?>js/jstz.min.js')); - var recheck = setInterval(function() { - if (window.jstz !== undefined) { - clearInterval(recheck); - var zone = jstz.determine(); - $('#timezone-dropdown').val(zone.name()).trigger('chosen:updated'); - - } - }, 200); - return false;"><i class="icon-map-marker"></i> <?php echo __('Auto Detect'); ?></button> + $TZ_TIMEZONE = $config['default_timezone']; + $TZ_NAME = 'default_timezone'; + $TZ_ALLOW_DEFAULT = false; + include STAFFINC_DIR.'templates/timezone.tmpl.php'; ?> + <div class="error"><?php echo $errors['default_timezone']; ?></div> </td> </tr> <tr><td width="220" class="required"><?php echo __('Date and Time Format');?>:</td> @@ -291,13 +274,9 @@ $gmtime = Misc::gmtime(); </p> </form> <script type="text/javascript"> -(function() { - $('#timezone-dropdown').chosen({ - allow_single_deselect: true, - width: '350px' +$(function() { + $('#secondary_langs').sortable({ + cursor: 'move' }); -})(); -$('#secondary_langs').sortable({ - cursor: 'move' }); </script> diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index 8e1c23723176dc71d50d3bccfcb6f23262741b8a..c2cf0f20077069397f978c21df38c73d4f8072af 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -7,6 +7,17 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <form action="settings.php?t=tickets" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="t" value="tickets" > + +<ul class="clean tabs"> + <li class="active"><a href="#settings"><i class="icon-asterisk"></i> + <?php echo __('Settings'); ?></a></li> + <li><a href="#autoresp"><i class="icon-mail-reply-all"></i> + <?php echo __('Autoresponder'); ?></a></li> + <li><a href="#alerts"><i class="icon-bell-alt"></i> + <?php echo __('Alerts and Notices'); ?></a></li> +</ul> + +<div class="tab_content" id="settings"> <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> @@ -279,6 +290,16 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <?php } ?> </tbody> </table> +</div> +<div class="hidden tab_content" id="autoresp" + data-tip-namespace="settings.autoresponder"> + <?php include STAFFINC_DIR . 'settings-autoresp.inc.php'; ?> +</div> +<div class="hidden tab_content" id="alerts" + data-tip-namespace="settings.alerts"> + <?php include STAFFINC_DIR . 'settings-alerts.inc.php'; ?> +</div> + <p style="padding-left:250px;"> <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes');?>"> <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes');?>"> diff --git a/include/staff/slaplan.inc.php b/include/staff/slaplan.inc.php index 3ca424c3044aea1d524f6ef8032b9671cfe04aca..484778e8bb49b76ddbe3abae7603a8675c907ec1 100644 --- a/include/staff/slaplan.inc.php +++ b/include/staff/slaplan.inc.php @@ -42,7 +42,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>" - data-translate-tag="<?php echo $trans['name']; ?>"/> + autofocus data-translate-tag="<?php echo $trans['name']; ?>"/> <span class="error">* <?php echo $errors['name']; ?></span> <i class="help-tip icon-question-sign" href="#name"></i> </td> </tr> diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php index e1e8fb461b01334a541d727c1948c86dab392e19..b18b7cd772b11206c46962cd0a2937f204163789 100644 --- a/include/staff/staff.inc.php +++ b/include/staff/staff.inc.php @@ -50,7 +50,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <input type="text" size="30" class="staff-username typeahead" - name="username" value="<?php echo $info['username']; ?>"> + autofocus name="username" value="<?php echo $info['username']; ?>"> <span class="error">* <?php echo $errors['username']; ?></span> <i class="help-tip icon-question-sign" href="#username"></i> </td> </tr> @@ -286,17 +286,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Time Zone');?>: </td> <td> - <select name="timezone" class="chosen-select" id="timezone-dropdown" - data-placeholder="<?php echo __('System Default'); ?>"> - <option value=""></option> -<?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($info['timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/',' / ',$zone); ?></option> -<?php } ?> - </select> - <span class="error">* <?php echo $errors['timezone']; ?></span> + <?php + $TZ_NAME = 'timezone'; + $TZ_TIMEZONE = $info['timezone']; + include STAFFINC_DIR.'templates/timezone.tmpl.php'; ?> + <div class="error"><?php echo $errors['timezone']; ?></div> </td> </tr> <tr> @@ -364,11 +358,3 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="staff.php"'> </p> </form> -<script type="text/javascript"> -!(function() { - $('#timezone-dropdown').chosen({ - allow_single_deselect: true, - width: '350px' - }); -})(); -</script> diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php index cd07dfa21bbd3edb44012f34cba80a06ecc9dd37..e74a543994d0823b9cabd010c021e293c36d1769 100644 --- a/include/staff/tasks.inc.php +++ b/include/staff/tasks.inc.php @@ -86,10 +86,19 @@ $tasks->filter(Q::any($visibility)); // Add in annotations $tasks->annotate(array( - //'collab_count' => SqlAggregate::COUNT('collaborators'), - 'attachment_count' => SqlAggregate::COUNT('thread__entries__attachments'), - 'thread_count' => SqlAggregate::COUNT('thread__entries'), - // 'isopen' => new SqlExpr(array('flags__hasbit' => TaskModel::ISOPEN)), + 'collab_count' => SqlAggregate::COUNT('thread__collaborators', true), + 'attachment_count' => SqlAggregate::COUNT(SqlCase::N() + ->when(new SqlField('thread__entries__attachments__inline'), null) + ->otherwise(new SqlField('thread__entries__attachments')), + true + ), + 'thread_count' => SqlAggregate::COUNT(SqlCase::N() + ->when( + new Q(array('thread__entries__flags__hasbit'=>ThreadEntry::FLAG_HIDDEN)), + null) + ->otherwise(new SqlField('thread__entries__id')), + true + ), )); $tasks->values('id', 'number', 'created', 'staff_id', 'team_id', diff --git a/include/staff/team.inc.php b/include/staff/team.inc.php index f851fdc3a9c789277eece37f62b60436530bd984..93719667251ffef9ff2ac99ea041907eb62a1add 100644 --- a/include/staff/team.inc.php +++ b/include/staff/team.inc.php @@ -31,7 +31,7 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); <i class="help-tip icon-question-sign" href="#teams"></i> </h2> <br> -<ul class="tabs"> +<ul class="clean tabs"> <li class="active"><a href="#team"> <i class="icon-file"></i> <?php echo __('Team'); ?></a></li> <?php @@ -58,7 +58,7 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); </td> <td> <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>" - data-translate-tag="<?php echo $trans['name']; ?>"/> + autofocus data-translate-tag="<?php echo $trans['name']; ?>"/> <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> diff --git a/include/staff/templates/advanced-search.tmpl.php b/include/staff/templates/advanced-search.tmpl.php index e9f275f027eb8093a9cf383db3b9b5a07aad354c..bb6153383b2f41063dc7084ce67ae281f4e5c1b7 100644 --- a/include/staff/templates/advanced-search.tmpl.php +++ b/include/staff/templates/advanced-search.tmpl.php @@ -2,7 +2,7 @@ $ff_uid = FormField::$uid; ?> <div id="advanced-search"> -<h3><?php echo __('Advanced Ticket Search');?></h3> +<h3 class="drag-handle"><?php echo __('Advanced Ticket Search');?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> <form action="#tickets/search" method="post" name="search"> diff --git a/include/staff/templates/collaborators-preview.tmpl.php b/include/staff/templates/collaborators-preview.tmpl.php index 964c225e7db174f8e0a614889104378f14d7e312..e7fb4f5985d6b46472e49e9453081d325c927b44 100644 --- a/include/staff/templates/collaborators-preview.tmpl.php +++ b/include/staff/templates/collaborators-preview.tmpl.php @@ -2,7 +2,7 @@ <table border="0" cellspacing="" cellpadding="1"> <colgroup><col style="min-width: 250px;"></col></colgroup> <?php -if (($users=$ticket->getCollaborators())) {?> +if (($users=$thread->getCollaborators())) {?> <?php foreach($users as $user) { echo sprintf('<tr><td %s><i class="icon-%s"></i> %s <em><%s></em></td></tr>', @@ -12,16 +12,16 @@ if (($users=$ticket->getCollaborators())) {?> $user->getEmail()); } } else { - echo "<strong>".__("Ticket doesn't have any collaborators.")."</strong>"; + echo "<strong>".__("Thread doesn't have any collaborators.")."</strong>"; }?> </table> <?php $options = array(); $options[] = sprintf( - '<a class="collaborators" id="managecollab" href="#tickets/%d/collaborators">%s</a>', - $ticket->getId(), - $ticket->getNumCollaborators() + '<a class="collaborators" id="managecollab" href="#thread/%d/collaborators">%s</a>', + $thread->getId(), + $thread->getNumCollaborators() ? __('Manage Collaborators') : __('Add Collaborator') ); diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php index d8d339c882dae25b1f5592f43a40c95420e76f66..c49b1b266edaf1118a5f058ab8cb2f67fa5dc8cb 100644 --- a/include/staff/templates/collaborators.tmpl.php +++ b/include/staff/templates/collaborators.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo __('Ticket Collaborators'); ?></h3> +<h3 class="drag-handle"><?php echo __('Collaborators'); ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <?php if($info && $info['msg']) { @@ -6,9 +6,9 @@ if($info && $info['msg']) { } ?> <hr/> <?php -if(($users=$ticket->getCollaborators())) {?> +if(($users=$thread->getCollaborators())) {?> <div id="manage_collaborators"> -<form method="post" class="collaborators" action="#tickets/<?php echo $ticket->getId(); ?>/collaborators"> +<form method="post" class="collaborators" action="#thread/<?php echo $thread->getId(); ?>/collaborators"> <table border="0" cellspacing="1" cellpadding="1" width="100%"> <?php foreach($users as $user) { @@ -16,7 +16,7 @@ if(($users=$ticket->getCollaborators())) {?> echo sprintf('<tr> <td> <input type="checkbox" name="cid[]" id="c%d" value="%d" %s> - <a class="collaborator" href="#collaborators/%d/view">%s</a> + <a class="collaborator" href="#thread/%d/collaborators/%d/view">%s</a> <span class="faded"><em>%s</em></span></td> <td width="10"> <input type="hidden" name="del[]" id="d%d" value=""> @@ -26,6 +26,7 @@ if(($users=$ticket->getCollaborators())) {?> $user->getId(), $user->getId(), $checked, + $thread->getId(), $user->getId(), Format::htmlchars($user->getName()), $user->getEmail(), @@ -36,7 +37,7 @@ if(($users=$ticket->getCollaborators())) {?> </table> <hr style="margin-top:1em"/> <div><a class="collaborator" - href="#tickets/<?php echo $ticket->getId(); ?>/add-collaborator" + href="#thread/<?php echo $thread->getId(); ?>/add-collaborator" ><i class="icon-plus-sign"></i> <?php echo __('Add New Collaborator'); ?></a></div> <div id="savewarning" style="display:none; padding-top:2px;"><p id="msg_warning"><?php echo __('You have made changes that you need to save.'); ?></p></div> @@ -57,15 +58,22 @@ if(($users=$ticket->getCollaborators())) {?> echo __("Bro, not sure how you got here!"); } -if ($_POST && $ticket && $ticket->getNumCollaborators()) { +if ($_POST && $thread && $thread->getNumCollaborators()) { + + $collaborators = sprintf('Participants (%d)', + $thread->getNumCollaborators()); + $recipients = sprintf(__('Recipients (%d of %d)'), - $ticket->getNumActiveCollaborators(), - $ticket->getNumCollaborators()); + $thread->getNumActiveCollaborators(), + $thread->getNumCollaborators()); ?> <script type="text/javascript"> $(function() { $('#emailcollab').show(); - $('#recipients').html('<?php echo $recipients; ?>'); + $('#t<?php echo $thread->getId(); ?>-recipients') + .html('<?php echo $recipients; ?>'); + $('#t<?php echo $thread->getId(); ?>-collaborators') + .html('<?php echo $collaborators; ?>'); }); </script> <?php diff --git a/include/staff/templates/content-manage.tmpl.php b/include/staff/templates/content-manage.tmpl.php index 3fa4e8fcbd8c1554649c576d84f3ba74e74e4eca..9009a4e45d157b292d25b9a233985e5b52c9aa7b 100644 --- a/include/staff/templates/content-manage.tmpl.php +++ b/include/staff/templates/content-manage.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo __('Manage Content'); ?> — <?php echo Format::htmlchars($content->getName()); ?></h3> +<h3 class="drag-handle"><?php echo __('Manage Content'); ?> — <?php echo Format::htmlchars($content->getName()); ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> @@ -27,7 +27,8 @@ if (count($langs) > 1) { ?> class="tab_content left-tabs" style="padding:0" lang="<?php echo $cfg->getPrimaryLanguage(); ?>"> <div class="error"><?php echo $errors['name']; ?></div> <input type="text" style="width: 100%; font-size: 14pt" name="name" value="<?php - echo Format::htmlchars($info['title']); ?>" /> + echo Format::htmlchars($info['title']); ?>" spellcheck="true" + lang="<?php echo $cfg->getPrimaryLanguage(); ?>" /> <div style="margin-top: 5px"> <div class="error"><?php echo $errors['body']; ?></div> <textarea class="richtext no-bar" name="body" @@ -46,7 +47,8 @@ if (count($langs) > 1) { ?> <input type="text" style="width: 100%; font-size: 14pt" name="trans[<?php echo $tag; ?>][title]" value="<?php echo Format::htmlchars($trans['title']); ?>" - placeholder="<?php echo __('Title'); ?>" /> + placeholder="<?php echo __('Title'); ?>" spellcheck="true" + lang="<?php echo $tag; ?>" /> <div style="margin-top: 5px"> <textarea class="richtext no-bar" data-direction=<?php echo $nfo['direction']; ?> data-root-context="<?php echo $content->getType(); ?>" diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php index f2e66643197866939fdb95fe1147c7b66ee1bca5..780fe18dec545d8d3ca8d760bcf1d21f31f3ad4a 100644 --- a/include/staff/templates/dynamic-field-config.tmpl.php +++ b/include/staff/templates/dynamic-field-config.tmpl.php @@ -1,4 +1,4 @@ - <h3><?php echo __('Field Configuration'); ?> — <?php echo $field->get('label') ?></h3> + <h3 class="drag-handle"><?php echo __('Field Configuration'); ?> — <?php echo $field->get('label') ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> <form method="post" action="#form/field-config/<?php diff --git a/include/staff/templates/form-manage.tmpl.php b/include/staff/templates/form-manage.tmpl.php index e002ef1654dc3273938aed81383ac5fe2ad4be20..4ad941887886ed77102a1b868d828e1fd79c5796 100644 --- a/include/staff/templates/form-manage.tmpl.php +++ b/include/staff/templates/form-manage.tmpl.php @@ -1,4 +1,4 @@ -<h3><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></i></h3> +<h3 class="drag-handle"><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></i></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/><?php echo __( 'Sort the forms on this ticket by click and dragging on them. Use the box below the forms list to add new forms to the ticket.' diff --git a/include/staff/templates/list-import.tmpl.php b/include/staff/templates/list-import.tmpl.php index b3c38a99d66b83973de5459acc6285769e02f625..bf367c76ce58d10d762aea0631f0fa03d034c06f 100644 --- a/include/staff/templates/list-import.tmpl.php +++ b/include/staff/templates/list-import.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/list-item-properties.tmpl.php b/include/staff/templates/list-item-properties.tmpl.php index 706c9484cb171e959788ce13a5c5cedb0624008e..61ef1b8f6ba3c0d25c3cfed5ceefd3040a1b8f18 100644 --- a/include/staff/templates/list-item-properties.tmpl.php +++ b/include/staff/templates/list-item-properties.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo $list->getName(); ?> — <?php +<h3 class="drag-handle"><?php echo $list->getName(); ?> — <?php echo $item ? $item->getValue() : __('Add New List Item'); ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> diff --git a/include/staff/templates/org-delete.tmpl.php b/include/staff/templates/org-delete.tmpl.php index 16e06c83b556e6bb2a034ba46d38c5c4834e3ec7..290d20e121932c75e3de195b957915e5bc378eac 100644 --- a/include/staff/templates/org-delete.tmpl.php +++ b/include/staff/templates/org-delete.tmpl.php @@ -6,7 +6,7 @@ if (!$info['title']) $info['warn'] = __('Deleted organization CANNOT be recovered'); ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/org-lookup.tmpl.php b/include/staff/templates/org-lookup.tmpl.php index 10a4ce28fbf75916ba718e52e45bd0c9f38ae25a..377cf53470893526e4664732d2f273e0c2ae1bef 100644 --- a/include/staff/templates/org-lookup.tmpl.php +++ b/include/staff/templates/org-lookup.tmpl.php @@ -9,7 +9,7 @@ if ($info['search'] === false) ?> <div id="the-lookup-form"> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <div><p id="msg_info"><i class="icon-info-sign"></i> <?php echo $msg_info; ?></p></div> @@ -17,7 +17,8 @@ if ($info['search'] === false) if ($info['search'] !== false) { ?> <div style="margin-bottom:10px;"> <input type="text" class="search-input" style="width:100%;" - placeholder="Search by name" id="org-search" autocorrect="off" autocomplete="off"/> + placeholder="Search by name" id="org-search" + autofocus autocorrect="off" autocomplete="off"/> </div> <?php } diff --git a/include/staff/templates/org-profile.tmpl.php b/include/staff/templates/org-profile.tmpl.php index c73a4f5a34d503a05a2ff8a2c2a5394bcdf1fdd7..90420dab8b8909fac2d7a53d1ed0e28a31b85376 100644 --- a/include/staff/templates/org-profile.tmpl.php +++ b/include/staff/templates/org-profile.tmpl.php @@ -4,7 +4,7 @@ $info=($_POST && $errors)?Format::input($_POST):@Format::htmlchars($org->getInfo if (!$info['title']) $info['title'] = Format::htmlchars($org->getName()); ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php @@ -170,6 +170,6 @@ $(function() { $('div#org-profile').fadeIn(); return false; }); - $("#primary_contacts").chosen({width: '300px'}); + $("#primary_contacts").select2({width: '300px'}); }); </script> diff --git a/include/staff/templates/org.tmpl.php b/include/staff/templates/org.tmpl.php index 41fcec1ee8d99b60b51446b7ea83e1216e10e16d..4b8bc97fa31cd6a8585bc944efb0d770c92f7532 100644 --- a/include/staff/templates/org.tmpl.php +++ b/include/staff/templates/org.tmpl.php @@ -2,7 +2,7 @@ if (!$info['title']) $info['title'] = Format::htmlchars($org->getName()); ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/sequence-manage.tmpl.php b/include/staff/templates/sequence-manage.tmpl.php index 10ac7391ba63dba74a9f07ccb21fbe15e2dfba58..d349605eecf985f516438e075318ea5076b83f2b 100644 --- a/include/staff/templates/sequence-manage.tmpl.php +++ b/include/staff/templates/sequence-manage.tmpl.php @@ -1,4 +1,4 @@ -<h3><i class="icon-wrench"></i> <?php echo __('Manage Sequences'); ?></i></h3> +<h3 class="drag-handle"><i class="icon-wrench"></i> <?php echo __('Manage Sequences'); ?></i></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/><?php echo __( 'Sequences are used to generate sequential numbers. Various sequences can be diff --git a/include/staff/templates/task-assign.tmpl.php b/include/staff/templates/task-assign.tmpl.php index 7019fa49a2b1d12fdb03039921f0f65b7f9d367c..1c0feca3a503dd433867579484d9751dbe16f846 100644 --- a/include/staff/templates/task-assign.tmpl.php +++ b/include/staff/templates/task-assign.tmpl.php @@ -7,7 +7,7 @@ if (!$info[':title']) $info[':title'] = sprintf(__('%s Selected Tasks'), __('Assign')); ?> -<h3><?php echo $info[':title']; ?></h3> +<h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> diff --git a/include/staff/templates/task-delete.tmpl.php b/include/staff/templates/task-delete.tmpl.php index 9a9191e4cc5134849fee8d60a5b4e0aa5d82c21a..eea44e19cc1710543a5febab3fefb648e20cb3b3 100644 --- a/include/staff/templates/task-delete.tmpl.php +++ b/include/staff/templates/task-delete.tmpl.php @@ -5,7 +5,7 @@ if (!$info[':title']) __('Delete'), _N('selected task', 'selected tasks', $count)); ?> -<h3><?php echo $info[':title']; ?></h3> +<h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> diff --git a/include/staff/templates/task-edit.tmpl.php b/include/staff/templates/task-edit.tmpl.php index a77079e251be82ab85d584083a05a540fe2eb977..b90d545f823a2e812da24300225643d69c70ed55 100644 --- a/include/staff/templates/task-edit.tmpl.php +++ b/include/staff/templates/task-edit.tmpl.php @@ -10,7 +10,7 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/edit'); ?> <div id="task-form"> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/task-preview.tmpl.php b/include/staff/templates/task-preview.tmpl.php index d6968f49a42da80daa6ccc8f730d548edbd519b0..5203956880e919bd708714227f1af258bb5206d5 100644 --- a/include/staff/templates/task-preview.tmpl.php +++ b/include/staff/templates/task-preview.tmpl.php @@ -23,6 +23,13 @@ echo '<ul class="tabs" id="task-preview">'; echo ' <li class="active"><a href="#summary" ><i class="icon-list-alt"></i> '.__('Task Summary').'</a></li>'; +if ($task->getThread()->getNumCollaborators()) { + echo sprintf(' + <li><a id="collab_tab" href="#collab" + ><i class="icon-fixed-width icon-group + faded"></i> '.__('Collaborators (%d)').'</a></li>', + $task->getThread()->getNumCollaborators()); +} echo '</ul>'; echo '<div id="task-preview_container">'; echo '<div class="tab_content" id="summary">'; @@ -72,9 +79,37 @@ echo ' </table>'; echo '</div>'; ?> -</div> <?php //TODO: add link to view if the user has permission - -echo '</div>'; ?> +<div class="hidden tab_content" id="collab"> + <table border="0" cellspacing="" cellpadding="1"> + <colgroup><col style="min-width: 250px;"></col></colgroup> + <?php + if (($collabs=$task->getThread()->getCollaborators())) {?> + <?php + foreach($collabs as $collab) { + echo sprintf('<tr><td %s><i class="icon-%s"></i> + <a href="users.php?id=%d" class="no-pjax">%s</a> <em><%s></em></td></tr>', + ($collab->isActive()? '' : 'class="faded"'), + ($collab->isActive()? 'comments' : 'comment-alt'), + $collab->getUserId(), + $collab->getName(), + $collab->getEmail()); + } + } else { + echo __("Task doesn't have any collaborators."); + }?> + </table> + <br> + <?php + echo sprintf('<span><a class="collaborators" + href="#thread/%d/collaborators">%s</a></span>', + $task->getThreadId(), + $task->getThread()->getNumCollaborators() + ? __('Manage Collaborators') : __('Add Collaborator') + ); + ?> +</div> +</div> +</div> diff --git a/include/staff/templates/task-status.tmpl.php b/include/staff/templates/task-status.tmpl.php index b711e6f91c71f2e8b46a6d353ca5ef2e859d5e39..d741aaafce72089c65299c2e83efd605a85c9086 100644 --- a/include/staff/templates/task-status.tmpl.php +++ b/include/staff/templates/task-status.tmpl.php @@ -5,7 +5,7 @@ if (!$info[':title']) $info[':title'] = __('Change Tasks Status'); ?> -<h3><?php echo $info[':title']; ?></h3> +<h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> diff --git a/include/staff/templates/task-transfer.tmpl.php b/include/staff/templates/task-transfer.tmpl.php index a45311f7f991b4fba4e0522272401edde44c376b..b199be14fcfcf90118081d4f4bb09d6861624e10 100644 --- a/include/staff/templates/task-transfer.tmpl.php +++ b/include/staff/templates/task-transfer.tmpl.php @@ -7,7 +7,7 @@ if (!$info[':title']) $info[':title'] = sprintf(__('%s Selected Tasks'), __('Tranfer')); ?> -<h3><?php echo $info[':title']; ?></h3> +<h3 class="drag-handle"><?php echo $info[':title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php index 7cd928b8a15e05921fe7e612fffd2e477fde94a2..0e3f509268b1b5dcded6377b5a137af6be80a029 100644 --- a/include/staff/templates/task-view.tmpl.php +++ b/include/staff/templates/task-view.tmpl.php @@ -61,22 +61,36 @@ if ($task->isOverdue()) ?> <table width="940" cellpadding="2" cellspacing="0" border="0"> <tr> - <td width="20%" class="has_bottom_border"> - <h2><a - id="reload-task" - href="tasks.php?id=<?php echo $task->getId(); ?>" - <?php - if ($ticket) { + <td width="<?php echo $ticket ? '70%' : '20%'; ?>" class="has_bottom_border"> + <?php + if ($ticket) { ?> + <strong> + <a id="tasks" href="#"> All Tasks (<?php echo $ticket->getNumTasks(); ?>)</a> + / + <?php echo $task->getTitle(); ?> + — + <a + id="reload-task" class="preview" + <?php echo ' class="preview" '; echo sprintf('data-preview="#tasks/%d/preview" ', $task->getId()); echo sprintf('href="#tasks/%d" ', $task->getId()); - } else { ?> - href="tasks.php?id=<?php echo $task->getId(); ?>" - <?php - } ?> + ?> + ><?php + echo sprintf('#%s', $task->getNumber()); ?></a> + </strong> + <?php + } else { ?> + <h2> + <a + id="reload-task" + href="tasks.php?id=<?php echo $task->getId(); ?>" + href="tasks.php?id=<?php echo $task->getId(); ?>" ><i class="icon-refresh"></i> <?php echo sprintf(__('Task #%s'), $task->getNumber()); ?></a> - </h2> + </h2> + <?php + } ?> </td> <td width="auto" class="flush-right has_bottom_border"> <?php @@ -88,7 +102,7 @@ if ($task->isOverdue()) <a class="task-action" href="#task-options"><i class="icon-reorder"></i> <?php - echo __('Options'); ?></a> + echo __('Actions'); ?></a> </span> <div id="action-dropdown-task-options" class="action-dropdown anchor-right"> @@ -143,6 +157,34 @@ if (!$ticket) { ?> <th width="100"><?php echo __('Status');?>:</th> <td><?php echo $task->getStatus(); ?></td> </tr> + + <tr> + <th><?php echo __('Create Date');?>:</th> + <td><?php echo Format::datetime($task->getCreateDate()); ?></td> + </tr> + <?php + if($task->isOpen()){ ?> + <tr> + <th><?php echo __('Due Date');?>:</th> + <td><?php echo $task->duedate ? + Format::datetime($task->duedate) : '<span + class="faded">— '.__('None').' —</span>'; ?></td> + </tr> + <?php + }else { ?> + <tr> + <th><?php echo __('Close Date');?>:</th> + <td><?php echo 0 ? + Format::datetime($task->getCloseDate()) : ''; ?></td> + </tr> + <?php + } + ?> + </table> + </td> + <td width="50%" style="vertical-align:top"> + <table cellspacing="0" cellpadding="4" width="100%" border="0"> + <tr> <th><?php echo __('Department');?>:</th> <td><?php echo Format::htmlchars($task->dept->getName()); ?></td> @@ -175,36 +217,24 @@ if (!$ticket) { ?> </tr> <?php } ?> - </table> - </td> - <td width="50%" style="vertical-align:top"> - <table cellspacing="0" cellpadding="4" width="100%" border="0"> - <tr> - <th><?php echo __('SLA Plan');?>:</th> - <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">— '.__('None').' —</span>'; ?></td> - </tr> - <tr> - <th><?php echo __('Create Date');?>:</th> - <td><?php echo Format::datetime($task->getCreateDate()); ?></td> - </tr> - <?php - if($task->isOpen()){ ?> <tr> - <th><?php echo __('Due Date');?>:</th> - <td><?php echo $task->duedate ? - Format::datetime($task->duedate) : '<span - class="faded">— '.__('None').' —</span>'; ?></td> - </tr> - <?php - }else { ?> - <tr> - <th><?php echo __('Close Date');?>:</th> - <td><?php echo 0 ? - Format::datetime($task->getCloseDate()) : ''; ?></td> + <th><?php echo __('Collaborators');?>:</th> + <td> + <?php + $collaborators = __('Add Participants'); + if ($task->getThread()->getNumCollaborators()) + $collaborators = sprintf(__('Participants (%d)'), + $task->getThread()->getNumCollaborators()); + + echo sprintf('<span><a class="collaborators preview" + href="#thread/%d/collaborators"><span + id="t%d-collaborators">%s</span></a></span>', + $task->getThreadId(), + $task->getThreadId(), + $collaborators); + ?> + </td> </tr> - <?php - } - ?> </table> </td> </tr> @@ -337,7 +367,7 @@ else <script type="text/javascript"> $(function() { - $(document).on('click', 'li.active a#ticket_tasks', function(e) { + $(document).on('click', 'li.active a#ticket_tasks, a#tasks', function(e) { e.preventDefault(); $('div#task_content').hide().empty(); $('div#tasks_content').show(); diff --git a/include/staff/templates/task.tmpl.php b/include/staff/templates/task.tmpl.php index d92da014f9f53e7367f67daecbfe3f41b517c45b..1271318ba3a5e48b7715ec5577987882e5fa1056 100644 --- a/include/staff/templates/task.tmpl.php +++ b/include/staff/templates/task.tmpl.php @@ -9,7 +9,7 @@ if ($ticket) ?> <div id="task-form"> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/thread-email-headers.tmpl.php b/include/staff/templates/thread-email-headers.tmpl.php index a84216ab46be6c4816a679ada4a1e77ff96e5216..631d43c2e225c09d0531e57fa589990dd134bb62 100644 --- a/include/staff/templates/thread-email-headers.tmpl.php +++ b/include/staff/templates/thread-email-headers.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo __('Raw Email Headers'); ?></h3> +<h3 class="drag-handle"><?php echo __('Raw Email Headers'); ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> diff --git a/include/staff/templates/thread-entries.tmpl.php b/include/staff/templates/thread-entries.tmpl.php index 821c1c50ca4416860e4350507fa730b744d6a2e9..2e7da2c389cf2b276b139a5573e040fb0e6590a9 100644 --- a/include/staff/templates/thread-entries.tmpl.php +++ b/include/staff/templates/thread-entries.tmpl.php @@ -35,7 +35,13 @@ if ($entries) { </div> <?php } ?> <span style="vertical-align:middle"> - <span style="vertical-align:middle;" class="textra"></span> + <span style="vertical-align:middle;" class="textra"> + <?php if ($entry->flags & ThreadEntry::FLAG_EDITED) { ?> + <span class="label label-bare" title="<?php + echo sprintf(__('Edited on %s by %s'), Format::datetime($entry->updated), 'You'); + ?>"><?php echo __('Edited'); ?></span> + <?php } ?> + </span> <span style="vertical-align:middle;" class="tmeta faded title"><?php echo Format::htmlchars($entry->getName()); ?></span> @@ -60,7 +66,8 @@ if ($entries) { Format::file_size($A->file->size)); ?> <a class="Icon file no-pjax" href="<?php echo $A->file->getDownloadUrl(); - ?>" target="_blank"><?php echo Format::htmlchars($A->file->name); + ?>" download="<?php echo Format::htmlchars($A->file->name); ?>" + target="_blank"><?php echo Format::htmlchars($A->file->name); ?></a><?php echo $size;?> <?php } ?> </td> diff --git a/include/staff/templates/thread-entry-edit.tmpl.php b/include/staff/templates/thread-entry-edit.tmpl.php index e1ed1f81c9e062f8c0a45bc4a1bc6c925759681f..24e63230e1c8bf91730369ed35907c5096eabfeb 100644 --- a/include/staff/templates/thread-entry-edit.tmpl.php +++ b/include/staff/templates/thread-entry-edit.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo __('Edit Thread Entry'); ?></h3> +<h3 class="drag-handle"><?php echo __('Edit Thread Entry'); ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> diff --git a/include/staff/templates/thread-entry-resend.tmpl.php b/include/staff/templates/thread-entry-resend.tmpl.php index 8a3e283c6418c0cdadd26b3db34a4fbc8c59f440..f5bc8eb9aa222b8a3569d357abf66bf9fcceb7f1 100644 --- a/include/staff/templates/thread-entry-resend.tmpl.php +++ b/include/staff/templates/thread-entry-resend.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo __('Resend Entry'); ?></h3> +<h3 class="drag-handle"><?php echo __('Resend Entry'); ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> diff --git a/include/staff/templates/thread-entry-view.tmpl.php b/include/staff/templates/thread-entry-view.tmpl.php index 2b76d851f3611a89e452492117593ebf17203b37..51a0a10da8bc411507c049337ee7cc2b92a86e5b 100644 --- a/include/staff/templates/thread-entry-view.tmpl.php +++ b/include/staff/templates/thread-entry-view.tmpl.php @@ -1,4 +1,4 @@ -<h3><?php echo __('Original Thread Entry'); ?></h3> +<h3 class="drag-handle"><?php echo __('Original Thread Entry'); ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> @@ -6,7 +6,21 @@ <?php $E = $entry; -do { ?> +$i = 0; +$omniscient = $thisstaff->getRole()->hasPerm(ThreadEntry::PERM_EDIT); +do { + $i++; + if (!$omniscient + // The current version is always visible + && $i > 1 + // If you originally posted it, you can see all the edits + && $E->staff_id != $thisstaff->getId() + // You can see your own edits + // && $E->editor != $thisstaff->getId() + ) { + // Skip edits made by other agents + continue; + } ?> <dt> <a href="#"><i class="icon-copy"></i> <strong><?php if ($E->title) @@ -48,11 +62,13 @@ $(function() { var allPanels = $('dd', A).hide().removeClass('hidden'); $('dt > a', A).click(function() { - $('dt', A).removeClass('active'); - allPanels.slideUp(); - $(this).parent().addClass('active').next().slideDown(); + if (!$(this).parent().is('.active')) { + $('dt', A).removeClass('active'); + allPanels.slideUp(); + $(this).parent().addClass('active').next().slideDown(); + } return false; }); - allPanels.last().show(); + allPanels.last().show().prev().addClass('active'); }, 100); }); diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php index 875c9d701bdcd641540b6f1c5d37e76b674ddc4c..f4091921f15fc4f32455e2fdd7aed9f94008f93d 100644 --- a/include/staff/templates/ticket-preview.tmpl.php +++ b/include/staff/templates/ticket-preview.tmpl.php @@ -34,12 +34,12 @@ echo '<ul class="tabs" id="ticket-preview">'; echo ' <li class="active"><a id="preview_tab" href="#preview" ><i class="icon-list-alt"></i> '.__('Ticket Summary').'</a></li>'; -if ($ticket->getNumCollaborators()) { +if ($ticket->getThread()->getNumCollaborators()) { echo sprintf(' <li><a id="collab_tab" href="#collab" ><i class="icon-fixed-width icon-group faded"></i> '.__('Collaborators (%d)').'</a></li>', - $ticket->getNumCollaborators()); + $ticket->getThread()->getNumCollaborators()); } echo '</ul>'; echo '<div id="ticket-preview_container">'; @@ -121,7 +121,7 @@ echo '</div>'; // ticket preview content. <table border="0" cellspacing="" cellpadding="1"> <colgroup><col style="min-width: 250px;"></col></colgroup> <?php - if (($collabs=$ticket->getCollaborators())) {?> + if (($collabs=$ticket->getThread()->getCollaborators())) {?> <?php foreach($collabs as $collab) { echo sprintf('<tr><td %s><i class="icon-%s"></i> @@ -141,7 +141,7 @@ echo '</div>'; // ticket preview content. echo sprintf('<span><a class="collaborators" href="#tickets/%d/collaborators">%s</a></span>', $ticket->getId(), - $ticket->getNumCollaborators() + $ticket->getThread()->getNumCollaborators() ? __('Manage Collaborators') : __('Add Collaborator') ); ?> diff --git a/include/staff/templates/ticket-print.tmpl.php b/include/staff/templates/ticket-print.tmpl.php index 11323c4a14b60077e7e208cc41f0b1434726882e..97074ed2682031b73672ecd817a5ba0085f30394 100644 --- a/include/staff/templates/ticket-print.tmpl.php +++ b/include/staff/templates/ticket-print.tmpl.php @@ -183,13 +183,10 @@ div.hr { <?php foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) { // Skip core fields shown earlier in the ticket view - // TODO: Rewrite getAnswers() so that one could write - // ->getAnswers()->filter(not(array('field__name__in'=> - // array('email', ...)))); - $answers = array_filter($form->getAnswers(), function ($a) { - return !in_array($a->getField()->get('name'), - array('email','subject','name','priority')); - }); + $answers = $form->getAnswers()->exclude(Q::any(array( + 'field__flags__hasbit' => DynamicFormField::FLAG_EXT_STORED, + 'field__name__in' => array('subject', 'priority') + ))); if (count($answers) == 0) continue; ?> diff --git a/include/staff/templates/ticket-status.tmpl.php b/include/staff/templates/ticket-status.tmpl.php index eae1b3272e99c586e0abdf88e08bdf9995a6c83b..458c6010c5b543ee79dd601af16ae43fa154bfbb 100644 --- a/include/staff/templates/ticket-status.tmpl.php +++ b/include/staff/templates/ticket-status.tmpl.php @@ -5,7 +5,7 @@ if (!$info['title']) $info['title'] = 'Change Tickets Status'; ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> diff --git a/include/staff/templates/timezone.tmpl.php b/include/staff/templates/timezone.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..840940b0ce92d9019847096d73334f4aa936a225 --- /dev/null +++ b/include/staff/templates/timezone.tmpl.php @@ -0,0 +1,39 @@ +<?php +$TZ_NAME = @$TZ_NAME ?: 'timezone'; +$TZ_ALLOW_DEFAULT = isset($TZ_ALLOW_DEFAULT) ? $TZ_ALLOW_DEFAULT : true; +$TZ_PLACEHOLDER = @$TZ_PLACEHOLDER ?: __('System Default'); +$TZ_TIMEZONE = @$TZ_TIMEZONE ?: ''; +?> +<select name="<?php echo $TZ_NAME; ?>" id="timezone-dropdown" + data-placeholder="<?php echo $TZ_PLACEHOLDER; ?>"> +<?php if ($TZ_ALLOW_DEFAULT) { ?> + <option value=""></option> +<?php } + foreach (DateTimeZone::listIdentifiers() as $zone) { ?> + <option value="<?php echo $zone; ?>" <?php + if ($TZ_TIMEZONE == $zone) + echo 'selected="selected"'; + ?>><?php echo str_replace('/',' / ',$zone); ?></option> +<?php } ?> + </select> + <button class="action-button" onclick="javascript: +$('head').append($('<script>').attr('src', '<?php + echo ROOT_PATH; ?>js/jstz.min.js')); +var recheck = setInterval(function() { + if (window.jstz !== undefined) { + clearInterval(recheck); + var zone = jstz.determine(); + $('#timezone-dropdown').val(zone.name()).trigger('change'); + + } +}, 100); +return false;" style="vertical-align:middle"><i class="icon-map-marker"></i> <?php echo __('Auto Detect'); ?></button> + +<script type="text/javascript"> +$(function() { + $('#timezone-dropdown').select2({ + allowClear: <?php echo $TZ_ALLOW_DEFAULT ? 'true' : 'false'; ?>, + width: '300px' + }); +}); +</script> diff --git a/include/staff/templates/user-account.tmpl.php b/include/staff/templates/user-account.tmpl.php index 6319aeb960daf8b427dc67169770b0e4f91d6100..ce1605f961cd27542a63df8589941f05469a8bd4 100644 --- a/include/staff/templates/user-account.tmpl.php +++ b/include/staff/templates/user-account.tmpl.php @@ -5,7 +5,7 @@ $access = (isset($info['_target']) && $info['_target'] == 'access'); if (!$info['title']) $info['title'] = Format::htmlchars($user->getName()); ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> @@ -67,16 +67,10 @@ if ($info['error']) { <?php echo __('Time Zone');?>: </td> <td> - <select name="timezone" class="chosen-select" id="timezone-dropdown" - data-placeholder="<?php echo __('System Default'); ?>"> - <option value=""></option> - <?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($info['timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/',' / ',$zone); ?></option> - <?php } ?> - </select> + <?php + $TZ_NAME = 'timezone'; + $TZ_TIMEZONE = $info['timezone']; + include STAFFINC_DIR.'templates/timezone.tmpl.php'; ?> <div class="error"><?php echo $errors['timezone']; ?></div> </td> </tr> @@ -174,11 +168,4 @@ $(function() { $('tbody#password').show(); }); }); -!(function() { - $('#timezone-dropdown').chosen({ - header: <?php echo JsonDataEncoder::encode(__('Time Zones')); ?>, - allow_single_deselect: true, - width: '350px' - }); -})(); </script> diff --git a/include/staff/templates/user-delete.tmpl.php b/include/staff/templates/user-delete.tmpl.php index 35bb685be93e07e0ca69efded5f0fe6c3466ff43..3c8ab2a6516cb1a190c19b501a237892d7d3ed0f 100644 --- a/include/staff/templates/user-delete.tmpl.php +++ b/include/staff/templates/user-delete.tmpl.php @@ -6,7 +6,7 @@ if (!$info['title']) $info['warn'] = __('Deleted users and tickets CANNOT be recovered'); ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/user-import.tmpl.php b/include/staff/templates/user-import.tmpl.php index 413ed54e4bcbaeed35f4a2a234e4d78722b4a96d..a1c47340b131e4487dbcae4e21451c381175f84d 100644 --- a/include/staff/templates/user-import.tmpl.php +++ b/include/staff/templates/user-import.tmpl.php @@ -1,5 +1,5 @@ <div id="the-lookup-form"> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php index 948026e8ff188f4ce6fb7ee5c1319db346226314..4a8e1c505bf782b04fec58c0b8a606958e10a6d2 100644 --- a/include/staff/templates/user-lookup.tmpl.php +++ b/include/staff/templates/user-lookup.tmpl.php @@ -1,5 +1,5 @@ <div id="the-lookup-form"> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr/> <?php @@ -10,9 +10,9 @@ if (!isset($info['lookup']) || $info['lookup'] !== false) { ?> : __('Search existing users.'); ?></p></div> <div style="margin-bottom:10px;"> - <input type="text" class="search-input" style="width:100%;" + <input type="search" class="search-input" style="width:100%;" placeholder="<?php echo __('Search by email, phone or name'); ?>" id="user-search" - autocorrect="off" autocomplete="off"/> + autofocus autocorrect="off" autocomplete="off"/> </div> <?php } diff --git a/include/staff/templates/user-register.tmpl.php b/include/staff/templates/user-register.tmpl.php index a68d1a23c17be5dcc3255a3d7955c638d275bd2d..1fee0e6f30c21ec81bd4a0c84bf7e4748406fa68 100644 --- a/include/staff/templates/user-register.tmpl.php +++ b/include/staff/templates/user-register.tmpl.php @@ -9,7 +9,7 @@ if (!$_POST) { } ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <div class="clear"></div> <hr/> @@ -130,17 +130,11 @@ echo sprintf(__( </tr> <td><?php echo __('Time Zone'); ?>:</td> <td> - <select name="timezone" class="chosen-select" id="timezone-dropdown" - data-placeholder="<?php echo __('System Default'); ?>"> - <option value=""></option> - <?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> - <option value="<?php echo $zone; ?>" <?php - if ($info['timezone'] == $zone) - echo 'selected="selected"'; - ?>><?php echo str_replace('/',' / ',$zone); ?></option> - <?php } ?> - </select> - <span class="error"><?php echo $errors['timezone']; ?></span> + <?php + $TZ_NAME = 'timezone'; + $TZ_TIMEZONE = $info['timezone']; + include STAFFINC_DIR.'templates/timezone.tmpl.php'; ?> + <div class="error"><?php echo $errors['timezone']; ?></div> </td> </tr> </tbody> @@ -166,9 +160,5 @@ $(function() { else $('tbody#password').show(); }); - $('#timezone-dropdown').chosen({ - allow_single_deselect: true, - width: '350px' - }); }); </script> diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php index 8c89749991d6bec12ebf2f901dacc57893ee324a..5add6b0824d389ff454ded25c57e9b83ce49717c 100644 --- a/include/staff/templates/user.tmpl.php +++ b/include/staff/templates/user.tmpl.php @@ -3,7 +3,7 @@ if (!isset($info['title'])) $info['title'] = Format::htmlchars($user->getName()); if ($info['title']) { ?> -<h3><?php echo $info['title']; ?></h3> +<h3 class="drag-handle"><?php echo $info['title']; ?></h3> <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> <hr> <?php diff --git a/include/staff/templates/users.tmpl.php b/include/staff/templates/users.tmpl.php index c8a262022a0d036d6127720354c77902ca59861d..a91a586f988889877c41d34331dfe2f9f589e341 100644 --- a/include/staff/templates/users.tmpl.php +++ b/include/staff/templates/users.tmpl.php @@ -159,7 +159,7 @@ if ($res && $num) { //Show options.. } ?> <div style="display:none;" class="dialog" id="confirm-action"> - <h3><?php echo __('Please Confirm'); ?></h3> + <h3 class="drag-handle"><?php echo __('Please Confirm'); ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> <p class="confirm-action" style="display:none;" id="remove-users-confirm"> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 35fb34acacdc3334b36e1ffd7381ad9d82b72bd3..74407ea1952c951da73f9345baa0dc6fe88ddb5a 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -213,10 +213,13 @@ if($ticket->isOverdue()) ><?php echo Format::htmlchars($ticket->getName()); ?></span></a> <?php - if($user) { - echo sprintf(' <a href="tickets.php?a=search&uid=%d" title="%s" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>', - urlencode($user->getId()), __('Related Tickets'), $user->getNumTickets()); - ?> + if ($user) { ?> + <a href="tickets.php?<?php echo Http::build_query(array( + 'status'=>'open', 'a'=>'search', 'uid'=> $user->getId() + )); ?>" title="<?php echo __('Related Tickets'); ?>" + data-dropdown="#action-dropdown-stats"> + (<b><?php echo $user->getNumTickets(); ?></b>) + </a> <div id="action-dropdown-stats" class="action-dropdown anchor-right"> <ul> <?php @@ -234,16 +237,48 @@ if($ticket->isOverdue()) <li><a href="users.php?id=<?php echo $user->getId(); ?>"><i class="icon-user icon-fixed-width"></i> <?php echo __('Manage User'); ?></a></li> -<?php if ($user->getOrgId()) { ?> +<?php } ?> + </ul> + </div> +<?php if ($user->getOrgId()) { ?> + <span style="display:inline-block"> + <i class="icon-building"></i> + <?php echo Format::htmlchars($user->getOrganization()->getName()); ?> + <a href="tickets.php?<?php echo Http::build_query(array( + 'status'=>'open', 'a'=>'search', 'orgid'=> $user->getOrgId() + )); ?>" title="<?php echo __('Related Tickets'); ?>" + data-dropdown="#action-dropdown-org-stats"> + (<b><?php echo $user->getNumOrganizationTickets(); ?></b>) + </a> + </span> + <div id="action-dropdown-org-stats" class="action-dropdown anchor-right"> + <ul> +<?php if ($open = $user->getNumOpenOrganizationTickets()) { ?> + <li><a href="tickets.php?<?php echo Http::build_query(array( + 'a' => 'search', 'status' => 'open', 'orgid' => $user->getOrgId() + )); ?>"><i class="icon-folder-open-alt icon-fixed-width"></i> + <?php echo sprintf(_N('%d Open Ticket', '%d Open Tickets', $open), $open); ?> + </a></li> +<?php } + if ($closed = $user->getNumClosedOrganizationTickets()) { ?> + <li><a href="tickets.php?<?php echo Http::build_query(array( + 'a' => 'search', 'status' => 'closed', 'orgid' => $user->getOrgId() + )); ?>"><i class="icon-folder-close-alt icon-fixed-width"></i> + <?php echo sprintf(_N('%d Closed Ticket', '%d Closed Tickets', $closed), $closed); ?> + </a></li> + <li><a href="tickets.php?<?php echo Http::build_query(array( + 'a' => 'search', 'orgid' => $user->getOrgId() + )); ?>"><i class="icon-double-angle-right icon-fixed-width"></i> <?php echo __('All Tickets'); ?></a></li> +<?php } + if ($thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY)) { ?> <li><a href="orgs.php?id=<?php echo $user->getOrgId(); ?>"><i class="icon-building icon-fixed-width"></i> <?php echo __('Manage Organization'); ?></a></li> -<?php } - } ?> +<?php } ?> </ul> </div> - <?php - } +<?php } # end if (user->org) + } # end if ($user) ?> </td> </tr> @@ -433,7 +468,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); </ul> <?php if ($role->hasPerm(TicketModel::PERM_REPLY)) { ?> - <form id="reply" class="tab_content" action="tickets.php?id=<?php + <form id="reply" class="tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>" name="reply" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> @@ -475,18 +510,19 @@ $tcount = $ticket->getThreadEntries($types)->count(); <td> <input type='checkbox' value='1' name="emailcollab" id="emailcollab" <?php echo ((!$info['emailcollab'] && !$errors) || isset($info['emailcollab']))?'checked="checked"':''; ?> - style="display:<?php echo $ticket->getNumCollaborators() ? 'inline-block': 'none'; ?>;" + style="display:<?php echo $ticket->getThread()->getNumCollaborators() ? 'inline-block': 'none'; ?>;" > <?php $recipients = __('Add Recipients'); - if ($ticket->getNumCollaborators()) + if ($ticket->getThread()->getNumCollaborators()) $recipients = sprintf(__('Recipients (%d of %d)'), - $ticket->getNumActiveCollaborators(), - $ticket->getNumCollaborators()); + $ticket->getThread()->getNumActiveCollaborators(), + $ticket->getThread()->getNumCollaborators()); echo sprintf('<span><a class="collaborators preview" - href="#tickets/%d/collaborators"><span id="recipients">%s</span></a></span>', - $ticket->getId(), + href="#thread/%d/collaborators"><span id="t%d-recipients">%s</span></a></span>', + $ticket->getThreadId(), + $ticket->getThreadId(), $recipients); ?> </td> @@ -622,7 +658,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); </form> <?php } ?> - <form id="note" class="hidden tab_content" action="tickets.php?id=<?php + <form id="note" class="hidden tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>#note" name="note" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> @@ -705,7 +741,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); </form> <?php if ($role->hasPerm(TicketModel::PERM_TRANSFER)) { ?> - <form id="transfer" class="hidden tab_content" action="tickets.php?id=<?php + <form id="transfer" class="hidden tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>#transfer" name="transfer" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="ticket_id" value="<?php echo $ticket->getId(); ?>"> @@ -766,7 +802,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); } ?> <?php if ($role->hasPerm(TicketModel::PERM_ASSIGN)) { ?> - <form id="assign" class="hidden tab_content" action="tickets.php?id=<?php + <form id="assign" class="hidden tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>#assign" name="assign" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> @@ -1010,5 +1046,5 @@ foreach (AttachmentFile::objects()->filter(array( 'filename' => $file->name, ); } ?> -$.showImagesInline(<?php echo JsonDataEncoder::encode($urls); ?>); +$('#ticket_thread').data('imageUrls', <?php echo JsonDataEncoder::encode($urls); ?>); </script> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 612f76f1759a62230fc2fc9a87410b7b9ed17ed7..518d40896878cf171c909ed3dbe64f0917b2a306 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -12,7 +12,7 @@ parse_str($_SERVER['QUERY_STRING'], $args); // Remove commands from query unset($args['id']); -unset($args['a']); +if ($args['a'] !== 'search') unset($args['a']); $refresh_url = $path . '?' . http_build_query($args); @@ -30,7 +30,8 @@ $sort_options = array( ); $use_subquery = true; -$queue_name = strtolower($_GET['status'] ?: $_GET['a']); //Status is overloaded +$queue_name = strtolower($_GET['a'] ?: $_GET['status']); //Status is overloaded + // Stash current queue view $_SESSION['::Q'] = $queue_name; @@ -69,6 +70,9 @@ case 'answered': break; default: case 'search': + $queue_sort_options = array('priority,updated', 'priority,created', + 'priority,due', 'due', 'updated', 'answered', + 'closed', 'number', 'hot'); // Consider basic search if ($_REQUEST['query']) { $results_type=__('Search Results'); @@ -95,9 +99,6 @@ case 'search': $view_all_tickets = $thisstaff->getRole()->hasPerm(SearchBackend::PERM_EVERYTHING); $results_type=__('Advanced Search') . '<a class="action-button" href="?clear_filter"><i style="top:0" class="icon-ban-circle"></i> <em>' . __('clear') . '</em></a>'; - $queue_sort_options = array('priority,updated', 'priority,created', - 'priority,due', 'due', 'updated', 'answered', - 'closed', 'number', 'hot'); $has_relevance = false; foreach ($tickets->getSortFields() as $sf) { if ($sf instanceof SqlCode && $sf->code == '`relevance`') { @@ -115,6 +116,21 @@ case 'search': break; } + // Apply user filter + elseif (isset($_GET['uid']) && ($user = User::lookup($_GET['uid']))) { + $tickets->filter(array('user__id'=>$_GET['uid'])); + $results_type = sprintf('%s — %s', __('Search Results'), + $user->getName()); + // Don't apply normal open ticket + break; + } + elseif (isset($_GET['orgid']) && ($org = Organization::lookup($_GET['orgid']))) { + $tickets->filter(array('user__org_id'=>$_GET['orgid'])); + $results_type = sprintf('%s — %s', __('Search Results'), + $org->getName()); + // Don't apply normal open ticket + break; + } // Fall-through and show open tickets case 'open': $status='open'; @@ -132,13 +148,9 @@ case 'open': break; } -// Apply user filter -if (isset($_GET['uid'])) { - $tickets->filter(array('user__id'=>$_GET['uid'])); -} - - // Apply primary ticket status +if (!isset($status) && isset($_GET['status'])) + $status = $_GET['status']; if ($status) $tickets->filter(array('status__state'=>$status)); @@ -290,13 +302,18 @@ $_SESSION[':Q:tickets'] = $orig_tickets; <!-- SEARCH FORM START --> <div id='basic_search'> - <form action="tickets.php" method="get"> + <form action="tickets.php" method="get" onsubmit="javascript: + $.pjax({ + url:$(this).attr('action') + '?' + $(this).serialize(), + container:'#pjax-container', + timeout: 2000 + }); +return false;"> <input type="hidden" name="a" value="search"> <table> <tr> - <td><input type="text" id="basic-ticket-search" name="query" - size=30 value="<?php echo Format::htmlchars($_REQUEST['query'], - true); ?>" + <td><input type="search" id="basic-ticket-search" name="query" + autofocus size="30" value="<?php echo Format::htmlchars($_REQUEST['query'], true); ?>" autocomplete="off" autocorrect="off" autocapitalize="off"></td> <td><input type="submit" class="button" value="<?php echo __('Search'); ?>"></td> <td> <a href="#" onclick="javascript: @@ -449,7 +466,8 @@ $_SESSION[':Q:tickets'] = $orig_tickets; href="tickets.php?id=<?php echo $T['ticket_id']; ?>"><span class="truncate"><?php echo $subject; ?></span></a> <?php if ($T['attachment_count']) - echo '<i class="small icon-paperclip icon-flip-horizontal"></i>'; + echo '<i class="small icon-paperclip icon-flip-horizontal" data-toggle="tooltip" title="' + .$T['attachment_count'].'"></i>'; if ($threadcount > 1) { ?> <span class="pull-right faded-more"><i class="icon-comments-alt"></i> <small><?php echo $threadcount; ?></small> @@ -458,7 +476,8 @@ $_SESSION[':Q:tickets'] = $orig_tickets; </td> <td nowrap><div><?php if ($T['collab_count']) - echo '<span class="pull-right faded-more"><i class="icon-group"></i></span>'; + echo '<span class="pull-right faded-more" data-toggle="tooltip" title="' + .$T['collab_count'].'"><i class="icon-group"></i></span>'; ?><span class="truncate" style="max-width:<?php echo $T['collab_count'] ? '150px' : '170px'; ?>"><?php $un = new PersonsName($T['user__name']); diff --git a/include/staff/user-view.inc.php b/include/staff/user-view.inc.php index 29c573a608649af3242abe7bee5a972615eac07a..256654d42816ec1d803c589dce7c00aa1dfde781 100644 --- a/include/staff/user-view.inc.php +++ b/include/staff/user-view.inc.php @@ -143,7 +143,7 @@ if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?> </table> <br> <div class="clear"></div> -<ul class="tabs" id="user-view-tabs"> +<ul class="clean tabs" id="user-view-tabs"> <li class="active"><a href="#tickets"><i class="icon-list-alt"></i> <?php echo __('User Tickets'); ?></a></li> <li><a href="#notes"><i diff --git a/include/staff/users.inc.php b/include/staff/users.inc.php index 848abf4dc1f35eed7bea90308af83504ab464b34..7e4993ad459d2e9bc63f57060fc11f0714c4ec15 100644 --- a/include/staff/users.inc.php +++ b/include/staff/users.inc.php @@ -63,8 +63,9 @@ $users->order_by($order . $order_column); <input type="hidden" name="a" value="search"> <table> <tr> - <td><input type="text" id="basic-user-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>" - autocomplete="off" autocorrect="off" autocapitalize="off"></td> + <td><input type="search" id="basic-user-search" name="query" + autofocus size="30" value="<?php echo Format::htmlchars($_REQUEST['query']); ?>" + autocomplete="off" autocorrect="off" autocapitalize="off"></td> <td><input type="submit" name="basic_search" class="button" value="<?php echo __('Search'); ?>"></td> <!-- <td> <a href="" id="advanced-user-search">[advanced]</a></td> --> </tr> diff --git a/index.php b/index.php index 97ff2378770c07cf0a2f3a58add652a7bea8c718..6c9558fa930d71173e59645febce619cf38651af 100644 --- a/index.php +++ b/index.php @@ -21,39 +21,8 @@ $section = 'home'; require(CLIENTINC_DIR.'header.inc.php'); ?> <div id="landing_page"> - <div class="sidebar pull-right"> - <div class="front-page-button flush-right"> -<p> - <a href="open.php" style="display:block" class="blue button"><?php - echo __('Open a New Ticket');?></a> -</p> - </div> - <div class="content"> -<?php - $faqs = FAQ::getFeatured()->select_related('category')->limit(5); - if ($faqs->all()) { ?> - <section><div class="header"><?php echo __('Featured Questions'); ?></div> -<?php foreach ($faqs as $F) { ?> - <div><a href="<?php echo ROOT_PATH; ?>/kb/faq.php?id=<?php - echo urlencode($F->getId()); - ?>"><?php echo $F->getLocalQuestion(); ?></a></div> -<?php } ?> - </section> -<?php - } - $resources = Page::getActivePages()->filter(array('type'=>'other')); - if ($resources->all()) { ?> - <section><div class="header"><?php echo __('Other Resources'); ?></div> -<?php foreach ($resources as $page) { ?> - <a href="<?php echo ROOT_PATH; ?>pages/<?php echo $page->getNameAsSlug(); - ?>"><?php echo $page->getLocalName(); ?></a> -<?php } ?> - </section> -<?php - } ?> - </div> - </div> -<div class="welcome"> +<?php include CLIENTINC_DIR.'templates/sidebar.tmpl.php'; ?> +<div class="main-content"> <?php if ($cfg && $cfg->isKnowledgebaseEnabled()) { ?> <div class="search-form"> @@ -63,6 +32,7 @@ if ($cfg && $cfg->isKnowledgebaseEnabled()) { ?> <button type="submit" class="green button">Search</button> </form> </div> + <div class="thread-body"> <?php } if($cfg && ($page = $cfg->getLandingPage())) @@ -70,6 +40,7 @@ if ($cfg && $cfg->isKnowledgebaseEnabled()) { ?> else echo '<h1>'.__('Welcome to the Support Center').'</h1>'; ?> + </div> </div> <div class="clear"></div> diff --git a/js/chosen.jquery.min.js b/js/chosen.jquery.min.js deleted file mode 100644 index 3c564f995a397b8b0315b44dbd3eb3956e1c57df..0000000000000000000000000000000000000000 --- a/js/chosen.jquery.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/* Chosen v1.2.0 | (c) 2011-2014 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ -!function(){var a,AbstractChosen,Chosen,SelectParser,b,c={}.hasOwnProperty,d=function(a,b){function d(){this.constructor=a}for(var e in b)c.call(b,e)&&(a[e]=b[e]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),children:0,disabled:a.disabled}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"<",">":">",'"':""","'":"'","`":"`"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0},AbstractChosen.prototype.set_default_text=function(){return this.default_text=this.form_field.getAttribute("data-placeholder")?this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(){var a=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return a.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(){var a=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return a.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f;for(b="",f=this.results_data,d=0,e=f.length;e>d;d++)c=f[d],b+=c.group?this.result_add_group(c):this.result_add_option(c),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(c.text));return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match?this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.style.cssText=a.style,c.setAttribute("data-option-array-index",a.array_index),c.innerHTML=a.search_text,this.outerHTML(c)):"":""},AbstractChosen.prototype.result_add_group=function(a){var b;return a.search_match||a.group_match?a.active_options>0?(b=document.createElement("li"),b.className="group-result",b.innerHTML=a.search_text,this.outerHTML(b)):"":""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.reset_single_select_options=function(){var a,b,c,d,e;for(d=this.results_data,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.selected?e.push(a.selected=!1):e.push(void 0);return e},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l;for(this.no_results_clear(),d=0,f=this.get_search_text(),a=f.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),i=new RegExp(a,"i"),c=this.get_search_regex(a),l=this.results_data,j=0,k=l.length;k>j;j++)b=l[j],b.search_match=!1,e=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(e=this.results_data[b.group_array_index],0===e.active_options&&e.search_match&&(d+=1),e.active_options+=1),(!b.group||this.group_search)&&(b.search_text=b.group?b.label:b.text,b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(d+=1),b.search_match?(f.length&&(g=b.search_text.search(i),h=b.search_text.substr(0,g+f.length)+"</em>"+b.search_text.substr(g+f.length),b.search_text=h.substr(0,g)+"<em>"+h.substr(g)),null!=e&&(e.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>d&&f.length?(this.update_results_content(""),this.no_results(f)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.get_search_regex=function(a){var b;return b=this.search_contains?"":"^",new RegExp(b+a,"i")},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.clipboard_event_checker=function(){var a=this;return setTimeout(function(){return a.results_search()},50)},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.prototype.search_results_touchstart=function(a){return this.touch_started=!0,this.search_results_mouseover(a)},AbstractChosen.prototype.search_results_touchmove=function(a){return this.touch_started=!1,this.search_results_mouseout(a)},AbstractChosen.prototype.search_results_touchend=function(a){return this.touch_started?this.search_results_mouseup(a):void 0},AbstractChosen.prototype.outerHTML=function(a){var b;return a.outerHTML?a.outerHTML:(b=document.createElement("div"),b.appendChild(a),b.innerHTML)},AbstractChosen.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),a=jQuery,a.fn.extend({chosen:function(b){return AbstractChosen.browser_is_supported()?this.each(function(){var c,d;c=a(this),d=c.data("chosen"),"destroy"===b&&d instanceof Chosen?d.destroy():d instanceof Chosen||c.data("chosen",new Chosen(this,b))}):this}}),Chosen=function(c){function Chosen(){return b=Chosen.__super__.constructor.apply(this,arguments)}return d(Chosen,c),Chosen.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field_jq.hasClass("chosen-rtl")},Chosen.prototype.set_up_html=function(){var b,c;return b=["chosen-container"],b.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&b.push(this.form_field.className),this.is_rtl&&b.push("chosen-rtl"),c={"class":b.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(c.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=a("<div />",c),this.is_multiple?this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>'):this.container.html('<a class="chosen-single chosen-default" tabindex="-1"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>'),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior(),this.form_field_jq.trigger("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.bind("touchstart.chosen",function(b){a.container_mousedown(b)}),this.container.bind("touchend.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mousedown.chosen",function(b){a.container_mousedown(b)}),this.container.bind("mouseup.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mouseenter.chosen",function(b){a.mouse_enter(b)}),this.container.bind("mouseleave.chosen",function(b){a.mouse_leave(b)}),this.search_results.bind("mouseup.chosen",function(b){a.search_results_mouseup(b)}),this.search_results.bind("mouseover.chosen",function(b){a.search_results_mouseover(b)}),this.search_results.bind("mouseout.chosen",function(b){a.search_results_mouseout(b)}),this.search_results.bind("mousewheel.chosen DOMMouseScroll.chosen",function(b){a.search_results_mousewheel(b)}),this.search_results.bind("touchstart.chosen",function(b){a.search_results_touchstart(b)}),this.search_results.bind("touchmove.chosen",function(b){a.search_results_touchmove(b)}),this.search_results.bind("touchend.chosen",function(b){a.search_results_touchend(b)}),this.form_field_jq.bind("chosen:updated.chosen",function(b){a.results_update_field(b)}),this.form_field_jq.bind("chosen:activate.chosen",function(b){a.activate_field(b)}),this.form_field_jq.bind("chosen:open.chosen",function(b){a.container_mousedown(b)}),this.form_field_jq.bind("chosen:close.chosen",function(b){a.input_blur(b)}),this.search_field.bind("blur.chosen",function(b){a.input_blur(b)}),this.search_field.bind("keyup.chosen",function(b){a.keyup_checker(b)}),this.search_field.bind("keydown.chosen",function(b){a.keydown_checker(b)}),this.search_field.bind("focus.chosen",function(b){a.input_focus(b)}),this.search_field.bind("cut.chosen",function(b){a.clipboard_event_checker(b)}),this.search_field.bind("paste.chosen",function(b){a.clipboard_event_checker(b)}),this.is_multiple?this.search_choices.bind("click.chosen",function(b){a.choices_click(b)}):this.container.bind("click.chosen",function(a){a.preventDefault()})},Chosen.prototype.destroy=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field_jq[0].disabled,this.is_disabled?(this.container.addClass("chosen-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus.chosen",this.activate_action),this.close_field()):(this.container.removeClass("chosen-disabled"),this.search_field[0].disabled=!1,this.is_multiple?void 0:this.selected_item.bind("focus.chosen",this.activate_action))},Chosen.prototype.container_mousedown=function(b){return this.is_disabled||(b&&"mousedown"===b.type&&!this.results_showing&&b.preventDefault(),null!=b&&a(b.target).hasClass("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!b||a(b.target)[0]!==this.selected_item[0]&&!a(b.target).parents("a.chosen-single").length||(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(this.container[0].ownerDocument).bind("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return a.originalEvent&&(b=a.originalEvent.deltaY||-a.originalEvent.wheelDelta||a.originalEvent.detail),null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop(b+this.search_results.scrollTop())):void 0},Chosen.prototype.blur_test=function(){return!this.active_field&&this.container.hasClass("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},Chosen.prototype.test_active_click=function(b){var c;return c=a(b.target).closest(".chosen-container"),c.length&&this.container[0]===c[0]?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){if(this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight(),b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(f>c)return this.search_results.scrollTop(c)}},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.container.addClass("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results(),this.form_field_jq.trigger("chosen:showing_dropdown",{chosen:this}))},Chosen.prototype.update_results_content=function(a){return this.search_results.html(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass("chosen-with-drop"),this.form_field_jq.trigger("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(){var a;return this.form_field.tabIndex?(a=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=a):void 0},Chosen.prototype.set_label_behavior=function(){var b=this;return this.form_field_label=this.form_field_jq.parents("label"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=a("label[for='"+this.form_field.id+"']")),this.form_field_label.length>0?this.form_field_label.bind("click.chosen",function(a){return b.is_multiple?b.container_mousedown(a):b.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},Chosen.prototype.search_results_mouseup=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c.length?(this.result_highlight=c,this.result_select(b),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c?this.result_do_highlight(c):void 0},Chosen.prototype.search_results_mouseout=function(b){return a(b.target).hasClass("active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(b){var c,d,e=this;return c=a("<li />",{"class":"search-choice"}).html("<span>"+b.html+"</span>"),b.disabled?c.addClass("search-choice-disabled"):(d=a("<a />",{"class":"search-choice-close","data-option-array-index":b.array_index}),d.bind("click.chosen",function(a){return e.choice_destroy_link_click(a)}),c.append(d)),this.search_container.before(c)},Chosen.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),b.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a(b.target))},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a[0].getAttribute("data-option-array-index"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.val().length<1&&this.results_hide(),a.parents("li").first().remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},Chosen.prototype.result_select=function(a){var b,c;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClass("active-result"):this.reset_single_select_options(),c=this.results_data[b[0].getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(c.text),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.search_field.val(""),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[c.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").text(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[b.options_index].value}),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.find("abbr").length||this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>'),this.selected_item.addClass("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return this.search_field.val()===this.default_text?"":a("<div/>").text(a.trim(this.search_field.val())).html()},Chosen.prototype.winnow_results_set_highlight=function(){var a,b;return b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first(),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(b){var c;return c=a('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),c.find("span").first().html(b),this.search_results.append(c),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a;return this.results_showing||this.is_multiple?this.result_highlight?(a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(a=this.search_container.siblings("li.search-choice").last(),a.length&&!a.hasClass("search-choice-disabled")?(this.pending_backstroke=a,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:this.results_showing&&a.preventDefault();break;case 32:this.disable_search&&a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){for(d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],i=0,j=g.length;j>i;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return b=a("<div />",{style:f}),b.text(this.search_field.val()),a("body").append(b),h=b.width()+25,b.remove(),c=this.container.outerWidth(),h>c-10&&(h=c-10),this.search_field.css({width:h+"px"})}},Chosen}(AbstractChosen)}.call(this); \ No newline at end of file diff --git a/js/filedrop.field.js b/js/filedrop.field.js index fc99870bdfd5fbe41d7679666344a1bd19ff3f78..4e82280aeeb894a8249c8723fefd22570635d1c8 100644 --- a/js/filedrop.field.js +++ b/js/filedrop.field.js @@ -349,8 +349,8 @@ this.on('drop', drop).on('dragstart', opts.dragStart).on('dragenter', dragEnter).on('dragover', dragOver).on('dragleave', dragLeave); $(document).on('drop', docDrop).on('dragenter', docEnter).on('dragover', docOver).on('dragleave', docLeave); - (opts.link || this).on('click', function(e){ - $('#' + opts.fallback_id).trigger(e); + (opts.link || this).click(function(e) { + $('#' + opts.fallback_id).trigger('click'); return false; }); diff --git a/js/osticket.js b/js/osticket.js index 911cbf4e758ab7b185837dfe8a51855e692d48c4..28fd56fc8fd5df9153eac06242e8116b8cdf5dc0 100644 --- a/js/osticket.js +++ b/js/osticket.js @@ -161,7 +161,9 @@ showImagesInline = function(urls, thread_id) { } ).append($('<div class="caption">') .append('<span class="filename">'+info.filename+'</span>') - .append('<a href="'+info.download_url+'" class="action-button pull-right"><i class="icon-download-alt"></i> ' + __('Download') + '</a>') + .append($('<a href="'+info.download_url+'" class="action-button pull-right"><i class="icon-download-alt"></i> ' + __('Download') + '</a>') + .attr('download', info.filename) + ) ); e.data('wrapped', true); } diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index 23b1ee76a4aaed7305df84ed4f974294dbe53139..38ca07f2c3e2a5f2dec0b8814415d52177a2f48d 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -53,8 +53,8 @@ RedactorPlugins.draft = function() { // Add [Delete Draft] button to the toolbar if (this.opts.draftDelete) { var trash = this.draft.deleteButton = - this.button.add('deleteDraft', __('Delete Draft'), - this.draft.deleteDraft); + this.button.add('deleteDraft', __('Delete Draft')) + this.button.addCallback(trash, this.draft.deleteDraft); this.button.setAwesome('deleteDraft', 'icon-trash'); trash.parent().addClass('pull-right'); trash.addClass('delete-draft'); @@ -268,6 +268,14 @@ $(function() { 'tabFocus': false, 'toolbarFixedBox': true, 'focusCallback': function() { this.$box.addClass('no-pjax'); }, + 'initCallback': function() { + if (this.$element.data('width')) + this.$editor.width(this.$element.data('width')); + this.$editor.attr('spellcheck', 'true'); + var lang = this.$editor.closest('[lang]').attr('lang'); + if (lang) + this.$editor.attr('lang', lang); + }, 'linkSize': 100000, 'definedLinks': 'ajax.php/config/links' }, options||{}); diff --git a/js/redactor-plugins.js b/js/redactor-plugins.js index 772573bab655b84260ba3b706921f0115a47e254..33edff821410ffa9c3dd136beed65170e145a0b5 100644 --- a/js/redactor-plugins.js +++ b/js/redactor-plugins.js @@ -93,7 +93,7 @@ RedactorPlugins.fontcolor = function() { var color = colors[z]; - var $swatch = $('<a rel="' + color + '" data-rule="' + rule +'" href="#" style="float: left; font-size: 0; border: 2px solid #fff; padding: 0; margin: 0; width: 22px; height: 22px;"></a>'); + var $swatch = $('<a rel="' + color + '" data-rule="' + rule +'" href="#" style="float: left; font-size: 0; border: 2px solid #fff; padding: 0; margin: 0; width: 22px; height: 22px; box-sizing: border-box;"></a>'); $swatch.css('background-color', color); $swatch.on('click', func); @@ -790,16 +790,35 @@ RedactorPlugins.imagepaste = function() { return true; this.$editor.on('paste.imagepaste', $.proxy(this.imagepaste.buildEventPaste, this)); + + // Capture the selection position every so often as Redactor seems to + // drop it when attempting an image paste before `paste` browser event + // fires + var that = this, + plugin = this.imagepaste; + setInterval(function() { + if (plugin.inpaste) + return; + plugin.offset = that.caret.getOffset() || plugin.offset; + }, 300); }, + offset: 0, + inpaste: false, buildEventPaste: function(e) { var event = e.originalEvent || e, fileUpload = false, files = [], i, file, - cd = event.clipboardData; + plugin = this.imagepaste, + cd = event.clipboardData, + self = this, node, + bail = function() { + plugin.inpaste = false; + }; + plugin.inpaste = true; - if (typeof(cd) === 'undefined') return; + if (typeof(cd) === 'undefined') return bail(); if (cd.items && cd.items.length) { @@ -824,23 +843,41 @@ RedactorPlugins.imagepaste = function() { } } } - var self = this, node; - this.opts.imageUploadCallback = function(image, json) { - // Redactor just has a bloody hard time inserting for some dumb - // reason. - }; - if (files.length) { - // clipboard upload - var I = setInterval(function() { - if (!self.focus.isFocused()) - return; - clearInterval(I); - self.clean.singleLine = false; - for (i = 0, k = files.length; i < k; i++) - self.upload.directUpload(files[i], e); - }, 5); - } + if (!files.length) + return bail(); + + // Clipboard upload + + setTimeout(function() { + // We need to allow the paste operation to settle, so we can set + // self.clean.singleLine and not have to cleared by some other running + // code + + var oldIUC = self.opts.imageUploadCallback; + self.opts.imageUploadCallback = function(image, json) { + self.$editor.find('.-image-upload-placeholder').remove(); + self.opts.imageUploadCallback = oldIUC; + // Add a zero-width space so that the caret:getOffset will find + // locations after pictures if only <br> tags exist otherwise. In + // other words, ensure there is at least one character after the + // image for text character counting. Additionally, Redactor will + // strip the zero-width space when saving + $(document.createTextNode("\u200b")).insertAfter($(image)); + bail(); + }; + + // Place the cursor back in the box! + self.caret.setOffset(plugin.offset); + + // Add cool wait cursor + self.insert.htmlWithoutClean('<span class="-image-upload-placeholder icon-stack"><i class="icon-circle icon-stack-base"></i><i class="icon-picture icon-light icon-spin"></i></span>'); + + // Upload clipboard files + self.clean.singleLine = false; + for (i = 0, k = files.length; i < k; i++) + self.upload.directUpload(files[i], e); + }, 1); } }; }; @@ -1766,11 +1803,13 @@ RedactorPlugins.contexttypeahead = function() { } }, - select: function(item) { + select: function(item, event) { var current = this.selection.getCurrent(), sel = this.selection.get(), range = this.sel.getRangeAt(0), cursorAt = range.endOffset, + // TODO: Consume immediately following `}` symbols + plugin = this.contexttypeahead, search = new RegExp(/%\{([^}]*)(\}?)$/); // FIXME: ENTER will end up here, but current will be empty @@ -1781,7 +1820,9 @@ RedactorPlugins.contexttypeahead = function() { // Set cursor at the end of the expanded text var left = current.textContent.substring(0, cursorAt), right = current.textContent.substring(cursorAt), - newLeft = left.replace(search, '%{' + item.variable + '}'); + autoExpand = event.target.nodeName == 'I', + selected = item.variable + (autoExpand ? '.' : '') + newLeft = left.replace(search, '%{' + selected + '}'); current.textContent = newLeft // Drop the remaining part of a variable block, if any @@ -1790,7 +1831,12 @@ RedactorPlugins.contexttypeahead = function() { this.range.setStart(current, newLeft.length - 1); this.range.setEnd(current, newLeft.length - 1); this.selection.addRange(); - return this.contexttypeahead.destroy(); + if (!autoExpand) + return plugin.destroy(); + + plugin.typeahead.val(selected); + plugin.typeahead.typeahead('lookup'); + return false; } }; }; diff --git a/js/select2.full.min.js b/js/select2.full.min.js new file mode 100644 index 0000000000000000000000000000000000000000..e311779ea07982aa4cad467adcfc14b072076557 --- /dev/null +++ b/js/select2.full.min.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){return n.apply(b,v.call(arguments,0).concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice;this.listeners=this.listeners||{},a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),this.$results.append(d)},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";{a(h)}this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b){var c=this,d=b.id+"-results";this.$results.attr("id",d),b.on("results:all",function(a){c.clear(),c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("results:append",function(a){c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("query",function(a){c.showLoading(a)}),b.on("select",function(){b.isOpen()&&c.setClasses()}),b.on("unselect",function(){b.isOpen()&&c.setClasses()}),b.on("open",function(){c.$results.attr("aria-expanded","true"),c.$results.attr("aria-hidden","false"),c.setClasses(),c.ensureHighlightVisible()}),b.on("close",function(){c.$results.attr("aria-expanded","false"),c.$results.attr("aria-hidden","true"),c.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=c.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=c.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?c.trigger("close"):c.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a);if(0!==d){var e=d-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top,h=f.offset().top,i=c.$results.scrollTop()+(h-g);0===e?c.$results.scrollTop(0):0>h-g&&c.$results.scrollTop(i)}}),b.on("results:next",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a),e=d+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top+c.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=c.$results.scrollTop()+h-g;0===e?c.$results.scrollTop(0):h>g&&c.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){c.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=c.$results.scrollTop(),d=c.$results.get(0).scrollHeight-c.$results.scrollTop()+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&d<=c.$results.height();e?(c.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(c.$results.scrollTop(c.$results.get(0).scrollHeight-c.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var d=a(this),e=d.data("data");return"true"===d.attr("aria-selected")?void(c.options.get("multiple")?c.trigger("unselect",{originalEvent:b,data:e}):c.trigger("close")):void c.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(){var b=a(this).data("data");c.getHighlightedResults().removeClass("select2-results__option--highlighted"),c.trigger("results:focus",{data:b,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a){var b=this,d=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){b.trigger("focus",a)}),this.$selection.on("blur",function(a){b.trigger("blur",a)}),this.$selection.on("keydown",function(a){b.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){b.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){b.update(a.data)}),a.on("open",function(){b.$selection.attr("aria-expanded","true"),b.$selection.attr("aria-owns",d),b._attachCloseHandler(a)}),a.on("close",function(){b.$selection.attr("aria-expanded","false"),b.$selection.removeAttr("aria-activedescendant"),b.$selection.removeAttr("aria-owns"),b.$selection.focus(),b._detachCloseHandler(a)}),a.on("enable",function(){b.$selection.attr("tabindex",b._tabindex)}),a.on("disable",function(){b.$selection.attr("tabindex","-1")})},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},d.prototype.bind=function(a){var b=this;d.__super__.bind.apply(this,arguments);var c=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",c),this.$selection.attr("aria-labelledby",c),this.$selection.on("mousedown",function(a){1===a.which&&b.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(){}),this.$selection.on("blur",function(){}),a.on("selection:update",function(a){b.update(a.data)})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){return a("<span></span>")},d.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.display(b),d=this.$selection.find(".select2-selection__rendered");d.empty().append(c),d.prop("title",b.title||b.text)},d}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(){var b=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){b.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(c){var d=a(this),e=d.parent(),f=e.data("data");b.trigger("unselect",{originalEvent:c,data:f})})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">×</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.display(e),g=this.selectionContainer();g.append(f),g.prop("title",e.title||e.text),g.data("data",e),b.push(g)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(){function a(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},a.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},a}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle")}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">×</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus()}),b.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val(""),e.$search.focus()}),b.on("enable",function(){e.$search.prop("disabled",!1)}),b.on("disable",function(){e.$search.prop("disabled",!0)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e.trigger("blur",a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}}),this.$selection.on("input",".select2-search--inline",function(){e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input",".select2-search--inline",function(a){e.handleSearch(a)})},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.trigger("open"),this.$search.val(b.text+" ")},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"â’¶":"A","A":"A","À":"A","Ã":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ä€":"A","Ä‚":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ç ":"A","Ä":"A","Çž":"A","Ả":"A","Ã…":"A","Ǻ":"A","Ç":"A","È€":"A","È‚":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ä„":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ç¢":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","â’·":"B","ï¼¢":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Æ‚":"B","Æ":"B","â’¸":"C","ï¼£":"C","Ć":"C","Ĉ":"C","ÄŠ":"C","ÄŒ":"C","Ç":"C","Ḉ":"C","Ƈ":"C","È»":"C","Ꜿ":"C","â’¹":"D","D":"D","Ḋ":"D","ÄŽ":"D","Ḍ":"D","á¸":"D","Ḓ":"D","Ḏ":"D","Ä":"D","Æ‹":"D","ÆŠ":"D","Ɖ":"D","ê¹":"D","DZ":"DZ","Ç„":"DZ","Dz":"Dz","Ç…":"Dz","â’º":"E","ï¼¥":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ä’":"E","Ḕ":"E","Ḗ":"E","Ä”":"E","Ä–":"E","Ë":"E","Ẻ":"E","Äš":"E","È„":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Æ":"E","ÆŽ":"E","â’»":"F","F":"F","Ḟ":"F","Æ‘":"F","ê»":"F","â’¼":"G","G":"G","Ç´":"G","Äœ":"G","Ḡ":"G","Äž":"G","Ä ":"G","Ǧ":"G","Ä¢":"G","Ǥ":"G","Æ“":"G","êž ":"G","ê½":"G","ê¾":"G","â’½":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Èž":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","â±µ":"H","êž":"H","â’¾":"I","I":"I","ÃŒ":"I","Ã":"I","ÃŽ":"I","Ĩ":"I","Ī":"I","Ĭ":"I","Ä°":"I","Ã":"I","Ḯ":"I","Ỉ":"I","Ç":"I","Ȉ":"I","ÈŠ":"I","Ị":"I","Ä®":"I","Ḭ":"I","Æ—":"I","â’¿":"J","J":"J","Ä´":"J","Ɉ":"J","â“€":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","ê€":"K","ê‚":"K","ê„":"K","Ꞣ":"K","â“":"L","L":"L","Ä¿":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ä»":"L","Ḽ":"L","Ḻ":"L","Å":"L","Ƚ":"L","â±¢":"L","â± ":"L","êˆ":"L","ê†":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","â“‚":"M","ï¼":"M","Ḿ":"M","á¹€":"M","Ṃ":"M","â±®":"M","Æœ":"M","Ⓝ":"N","ï¼®":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Å…":"N","Ṋ":"N","Ṉ":"N","È ":"N","Æ":"N","êž":"N","Ꞥ":"N","ÇŠ":"NJ","Ç‹":"Nj","â“„":"O","O":"O","Ã’":"O","Ó":"O","Ô":"O","á»’":"O","á»":"O","á»–":"O","á»”":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","ÅŒ":"O","á¹":"O","á¹’":"O","ÅŽ":"O","È®":"O","È°":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Å":"O","Ç‘":"O","ÈŒ":"O","ÈŽ":"O","Æ ":"O","Ờ":"O","Ớ":"O","á» ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","ÆŸ":"O","êŠ":"O","êŒ":"O","Æ¢":"OI","êŽ":"OO","È¢":"OU","â“…":"P","ï¼°":"P","á¹”":"P","á¹–":"P","Ƥ":"P","â±£":"P","ê":"P","ê’":"P","ê”":"P","Ⓠ":"Q","ï¼±":"Q","ê–":"Q","ê˜":"Q","ÉŠ":"Q","Ⓡ":"R","ï¼²":"R","Å”":"R","Ṙ":"R","Ř":"R","È":"R","È’":"R","Ṛ":"R","Ṝ":"R","Å–":"R","Ṟ":"R","ÉŒ":"R","Ɽ":"R","êš":"R","Ꞧ":"R","êž‚":"R","Ⓢ":"S","ï¼³":"S","ẞ":"S","Åš":"S","Ṥ":"S","Åœ":"S","á¹ ":"S","Å ":"S","Ṧ":"S","á¹¢":"S","Ṩ":"S","Ș":"S","Åž":"S","â±¾":"S","Ꞩ":"S","êž„":"S","Ⓣ":"T","ï¼´":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Èš":"T","Å¢":"T","á¹°":"T","á¹®":"T","Ŧ":"T","Ƭ":"T","Æ®":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","â“Š":"U","ï¼µ":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ãœ":"U","Ç›":"U","Ç—":"U","Ç•":"U","Ç™":"U","Ủ":"U","Å®":"U","Å°":"U","Ç“":"U","È”":"U","È–":"U","Ư":"U","Ừ":"U","Ứ":"U","á»®":"U","Ử":"U","á»°":"U","Ụ":"U","á¹²":"U","Ų":"U","Ṷ":"U","á¹´":"U","É„":"U","â“‹":"V","V":"V","á¹¼":"V","á¹¾":"V","Ʋ":"V","êž":"V","É…":"V","ê ":"VY","â“Œ":"W","ï¼·":"W","Ẁ":"W","Ẃ":"W","Å´":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","â±²":"W","â“":"X","X":"X","Ẋ":"X","Ẍ":"X","â“Ž":"Y","ï¼¹":"Y","Ỳ":"Y","Ã":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","á»´":"Y","Ƴ":"Y","ÉŽ":"Y","Ỿ":"Y","â“":"Z","Z":"Z","Ź":"Z","áº":"Z","Å»":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","ê¢":"Z","â“":"a","ï½":"a","ẚ":"a","à ":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","Ä":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","Ç¡":"a","ä":"a","ÇŸ":"a","ả":"a","Ã¥":"a","Ç»":"a","ÇŽ":"a","È":"a","ȃ":"a","ạ":"a","áº":"a","ặ":"a","á¸":"a","Ä…":"a","â±¥":"a","É":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","Ç£":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","â“‘":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","Æ€":"b","ƃ":"b","É“":"b","â“’":"c","c":"c","ć":"c","ĉ":"c","Ä‹":"c","Ä":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","â““":"d","d":"d","ḋ":"d","Ä":"d","á¸":"d","ḑ":"d","ḓ":"d","á¸":"d","Ä‘":"d","ÆŒ":"d","É–":"d","É—":"d","êº":"d","dz":"dz","dž":"dz","â“”":"e","ï½…":"e","è":"e","é":"e","ê":"e","á»":"e","ế":"e","á»…":"e","ể":"e","ẽ":"e","Ä“":"e","ḕ":"e","ḗ":"e","Ä•":"e","Ä—":"e","ë":"e","ẻ":"e","Ä›":"e","È…":"e","ȇ":"e","ẹ":"e","ệ":"e","È©":"e","á¸":"e","Ä™":"e","ḙ":"e","ḛ":"e","ɇ":"e","É›":"e","Ç":"e","â“•":"f","f":"f","ḟ":"f","Æ’":"f","ê¼":"f","â“–":"g","g":"g","ǵ":"g","Ä":"g","ḡ":"g","ÄŸ":"g","Ä¡":"g","ǧ":"g","Ä£":"g","Ç¥":"g","É ":"g","êž¡":"g","áµ¹":"g","ê¿":"g","â“—":"h","h":"h","Ä¥":"h","ḣ":"h","ḧ":"h","ÈŸ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","É¥":"h","Æ•":"hv","ⓘ":"i","i":"i","ì":"i","Ã":"i","î":"i","Ä©":"i","Ä«":"i","Ä":"i","ï":"i","ḯ":"i","ỉ":"i","Ç":"i","ȉ":"i","È‹":"i","ị":"i","į":"i","á¸":"i","ɨ":"i","ı":"i","â“™":"j","j":"j","ĵ":"j","Ç°":"j","ɉ":"j","â“š":"k","k":"k","ḱ":"k","Ç©":"k","ḳ":"k","Ä·":"k","ḵ":"k","Æ™":"k","ⱪ":"k","ê":"k","êƒ":"k","ê…":"k","ꞣ":"k","â“›":"l","l":"l","Å€":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","Å¿":"l","Å‚":"l","Æš":"l","É«":"l","ⱡ":"l","ê‰":"l","êž":"l","ê‡":"l","lj":"lj","â“œ":"m","ï½":"m","ḿ":"m","á¹":"m","ṃ":"m","ɱ":"m","ɯ":"m","â“":"n","n":"n","ǹ":"n","Å„":"n","ñ":"n","á¹…":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","Æž":"n","ɲ":"n","ʼn":"n","êž‘":"n","ꞥ":"n","ÇŒ":"nj","â“ž":"o","ï½":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","á»—":"o","ổ":"o","õ":"o","á¹":"o","È":"o","á¹":"o","Å":"o","ṑ":"o","ṓ":"o","Å":"o","ȯ":"o","ȱ":"o","ö":"o","È«":"o","á»":"o","Å‘":"o","Ç’":"o","È":"o","È":"o","Æ¡":"o","á»":"o","á»›":"o","ỡ":"o","ở":"o","ợ":"o","á»":"o","á»™":"o","Ç«":"o","Ç":"o","ø":"o","Ç¿":"o","É”":"o","ê‹":"o","ê":"o","ɵ":"o","Æ£":"oi","È£":"ou","ê":"oo","â“Ÿ":"p","ï½":"p","ṕ":"p","á¹—":"p","Æ¥":"p","áµ½":"p","ê‘":"p","ê“":"p","ê•":"p","â“ ":"q","q":"q","É‹":"q","ê—":"q","ê™":"q","â“¡":"r","ï½’":"r","Å•":"r","á¹™":"r","Å™":"r","È‘":"r","È“":"r","á¹›":"r","á¹":"r","Å—":"r","ṟ":"r","É":"r","ɽ":"r","ê›":"r","ꞧ":"r","ꞃ":"r","â“¢":"s","s":"s","ß":"s","Å›":"s","á¹¥":"s","Å":"s","ṡ":"s","Å¡":"s","ṧ":"s","á¹£":"s","ṩ":"s","È™":"s","ÅŸ":"s","È¿":"s","êž©":"s","êž…":"s","ẛ":"s","â“£":"t","ï½”":"t","ṫ":"t","ẗ":"t","Å¥":"t","á¹":"t","È›":"t","Å£":"t","á¹±":"t","ṯ":"t","ŧ":"t","Æ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","Å©":"u","á¹¹":"u","Å«":"u","á¹»":"u","Å":"u","ü":"u","Çœ":"u","ǘ":"u","Ç–":"u","Çš":"u","ủ":"u","ů":"u","ű":"u","Ç”":"u","È•":"u","È—":"u","Æ°":"u","ừ":"u","ứ":"u","ữ":"u","á»":"u","á»±":"u","ụ":"u","á¹³":"u","ų":"u","á¹·":"u","á¹µ":"u","ʉ":"u","â“¥":"v","ï½–":"v","á¹½":"v","ṿ":"v","Ê‹":"v","êŸ":"v","ÊŒ":"v","ê¡":"vy","ⓦ":"w","ï½—":"w","áº":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","â±³":"w","ⓧ":"x","x":"x","ẋ":"x","áº":"x","ⓨ":"y","ï½™":"y","ỳ":"y","ý":"y","Å·":"y","ỹ":"y","ȳ":"y","áº":"y","ÿ":"y","á»·":"y","ẙ":"y","ỵ":"y","Æ´":"y","É":"y","ỿ":"y","â“©":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","È¥":"z","É€":"z","ⱬ":"z","ê£":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","ÎŒ":"Ο","ÎŽ":"Î¥","Ϋ":"Î¥","Î":"Ω","ά":"α","Î":"ε","ή":"η","ί":"ι","ÏŠ":"ι","Î":"ι","ÏŒ":"ο","Ï":"Ï…","Ï‹":"Ï…","ΰ":"Ï…","ω":"ω","Ï‚":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a){var b=this;this.container=a,a.on("select",function(a){b.select(a.data)}),a.on("unselect",function(a){b.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={}; +if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=(c.extend(!0,{},l,j),this.option(l));k.replaceWith(m)}else{var n=this.option(j);if(j.children){var o=this.convertToOptions(j.children);b.appendMany(n,o)}h.push(n)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(b,c){this.ajaxOptions=this._applyDefaults(c.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),a.__super__.constructor.call(this,b,c)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return{q:a.term}},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url(a)),"function"==typeof f.data&&(f.data=f.data(a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");if(void 0!==f&&(this.createTag=f),b.call(this,c,d),a.isArray(e))for(var g=0;g<e.length;g++){var h=e[g],i=this._normalizeItem(h),j=this.option(i);this.$element.append(j)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(){var b=(this._lastTag,this.$element.find("option[data-select2-tag]"));b.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(a,b,c){function d(a){e.select(a)}var e=this;b.term=b.term||"";var f=this.tokenizer(b,this.options,d);f.term!==b.term&&(this.$search.length&&(this.$search.val(f.term),this.$search.focus()),b.term=f.term),a.call(this,b,c)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);e(m),g=g.substr(h+1)||"",h=0}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.position=function(){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a){function b(){}return b.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},b.prototype.handleSearch=function(){if(!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},b.prototype.showSearch=function(){return!0},b}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="option load-more" role="treeitem"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(a,b,c){this.$dropdownParent=c.get("dropdownParent")||document.body,a.call(this,b,c)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c){var d=this,e="scroll.select2."+c.id,f="resize.select2."+c.id,g="orientationchange.select2."+c.id,h=this.$container.parents().filter(b.hasScroll);h.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),h.on(e,function(){var b=a(this).data("select2-scroll-position");a(this).scrollTop(b.y)}),a(window).on(e+" "+f+" "+g,function(){d._positionDropdown(),d._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c){var d="scroll.select2."+c.id,e="resize.select2."+c.id,f="orientationchange.select2."+c.id,g=this.$container.parents().filter(b.hasScroll);g.off(d),a(window).off(d+" "+e+" "+f)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=(this.$container.position(),this.$container.offset());f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom};c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){this.$dropdownContainer.width(),this.$dropdown.css({width:this.$container.outerWidth(!1)+"px"})},c.prototype._showDropdown=function(){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(){d._handleSelectOnClose()})},a.prototype._handleSelectOnClose=function(){var a=this.getHighlightedResults();a.length<1||this.trigger("select",{data:a.data("data")})},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close")},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend({},this.defaults,l),null==l.dataAdapter){if(l.dataAdapter=null!=l.ajax?o:null!=l.data?n:m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.selectionAdapter=l.multiple?e:d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(this.options.dir=a.prop("dir")?a.prop("dir"):a.closest("[dir]").prop("dir")?a.closest("[dir]").prop("dir"):"ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("focus",function(){a.$container.addClass("select2-container--focus")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open"),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ENTER?(a.trigger("results:select"),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle"),b.preventDefault()):c===d.UP?(a.trigger("results:previous"),b.preventDefault()):c===d.DOWN?(a.trigger("results:next"),b.preventDefault()):(c===d.ESC||c===d.TAB)&&(a.close(),b.preventDefault()):(c===d.ENTER||c===d.SPACE||(c===d.DOWN||c===d.UP)&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable")):this.trigger("enable")},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||(this.trigger("query",{}),this.trigger("open"))},e.prototype.close=function(){this.isOpen()&&this.trigger("close")},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("select2/compat/utils",["jquery"],function(a){function b(b,c,d){var e,f,g=[];e=a.trim(b.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0===this.indexOf("select2-")&&g.push(this)})),e=a.trim(c.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&(f=d(this),null!=f&&g.push(f))})),b.attr("class",g.join(" "))}return{syncCssClasses:b}}),b.define("select2/compat/containerCss",["jquery","./utils"],function(a,b){function c(){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("containerCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptContainerCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("containerCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/dropdownCss",["jquery","./utils"],function(a,b){function c(){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("dropdownCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptDropdownCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("dropdownCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/initSelection",["jquery"],function(a){function b(a,b,c){c.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=c.get("initSelection"),this._isInitialized=!1,a.call(this,b,c)}return b.prototype.current=function(b,c){var d=this;return this._isInitialized?void b.call(this,c):void this.initSelection.call(null,this.$element,function(b){d._isInitialized=!0,a.isArray(b)||(b=[b]),c(b)})},b}),b.define("select2/compat/inputData",["jquery"],function(a){function b(a,b,c){this._currentData=[],this._valueSeparator=c.get("valueSeparator")||",","hidden"===b.prop("type")&&c.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a `<select>` element instead."),a.call(this,b,c)}return b.prototype.current=function(b,c){function d(b,c){var e=[];return b.selected||-1!==a.inArray(b.id,c)?(b.selected=!0,e.push(b)):b.selected=!1,b.children&&e.push.apply(e,d(b.children,c)),e}for(var e=[],f=0;f<this._currentData.length;f++){var g=this._currentData[f];e.push.apply(e,d(g,this.$element.val().split(this._valueSeparator)))}c(e)},b.prototype.select=function(b,c){if(this.options.get("multiple")){var d=this.$element.val();d+=this._valueSeparator+c.id,this.$element.val(d),this.$element.trigger("change")}else this.current(function(b){a.map(b,function(a){a.selected=!1})}),this.$element.val(c.id),this.$element.trigger("change")},b.prototype.unselect=function(a,b){var c=this;b.selected=!1,this.current(function(a){for(var d=[],e=0;e<a.length;e++){var f=a[e];b.id!=f.id&&d.push(f.id)}c.$element.val(d.join(c._valueSeparator)),c.$element.trigger("change") +})},b.prototype.query=function(a,b,c){for(var d=[],e=0;e<this._currentData.length;e++){var f=this._currentData[e],g=this.matches(b,f);null!==g&&d.push(g)}c({results:d})},b.prototype.addOptions=function(b,c){var d=a.map(c,function(b){return a.data(b[0],"data")});this._currentData.push.apply(this._currentData,d)},b}),b.define("select2/compat/matcher",["jquery"],function(a){function b(b){function c(c,d){var e=a.extend(!0,{},d);if(null==c.term||""===a.trim(c.term))return e;if(d.children){for(var f=d.children.length-1;f>=0;f--){var g=d.children[f],h=b(c.term,g.text,g);h||e.children.splice(f,1)}if(e.children.length>0)return e}return b(c.term,d.text,d)?e:null}return c}return b}),b.define("select2/compat/query",[],function(){function a(a,b,c){c.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `query` option has been deprecated in favor of a custom data adapter that overrides the `query` method. Support will be removed for the `query` option in future versions of Select2."),a.call(this,b,c)}return a.prototype.query=function(a,b,c){b.callback=c;var d=this.options.get("query");d.call(null,b)},a}),b.define("select2/dropdown/attachContainer",[],function(){function a(a,b,c){a.call(this,b,c)}return a.prototype.position=function(a,b,c){var d=c.find(".dropdown-wrapper");d.append(b),b.addClass("select2-dropdown--below"),c.addClass("select2-container--below")},a}),b.define("select2/dropdown/stopPropagation",[],function(){function a(){}return a.prototype.bind=function(a,b,c){a.call(this,b,c);var d=["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"];this.$dropdown.on(d.join(" "),function(a){a.stopPropagation()})},a}),b.define("select2/selection/stopPropagation",[],function(){function a(){}return a.prototype.bind=function(a,b,c){a.call(this,b,c);var d=["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"];this.$selection.on(d.join(" "),function(a){a.stopPropagation()})},a}),b.define("jquery.select2",["jquery","require","./select2/core","./select2/defaults"],function(a,b,c,d){if(b("jquery.mousewheel"),null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){{var d=a.extend({},b,!0);new c(a(this),d)}}),this;if("string"==typeof b){var d=this.data("select2");null==d&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var f=Array.prototype.slice.call(arguments,1),g=d[b](f);return a.inArray(b,e)>-1?this:g}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),function(c){"function"==typeof b.define&&b.define.amd?b.define("jquery.mousewheel",["jquery"],c):"object"==typeof exports?module.exports=c:c(a)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c}); \ No newline at end of file diff --git a/pages/index.php b/pages/index.php index ab50a1c586d5793301fdcb0246842c31eba95ec2..e9de487346c43d950020397bdc2df70250d8e526 100644 --- a/pages/index.php +++ b/pages/index.php @@ -28,27 +28,35 @@ $slug = Format::slugify($ost->get_path_info()); $first_word = explode('-', $slug); $first_word = $first_word[0]; -$sql = 'SELECT id, name FROM '.PAGE_TABLE - .' WHERE name LIKE '.db_input("$first_word%"); -$page_id = null; - -$res = db_query($sql); -while (list($id, $name) = db_fetch_row($res)) { - if (Format::slugify($name) == $slug) { - $page_id = $id; +$pages = Page::objects()->filter(array( + 'name__like' => "$first_word%" +)); + +$selected_page = null; +foreach ($pages as $P) { + if (Format::slugify($P->name) == $slug) { + $selected_page = $P; break; } } -if (!$page_id || !($page = Page::lookup($page_id))) +if (!$selected_page) Http::response(404, __('Page Not Found')); -if (!$page->isActive() || $page->getType() != 'other') +if (!$selected_page->isActive() || $selected_page->getType() != 'other') Http::response(404, __('Page Not Found')); require(CLIENTINC_DIR.'header.inc.php'); -print $page->getBodyWithImages(); +$BUTTONS = false; +include CLIENTINC_DIR.'templates/sidebar.tmpl.php'; +?> +<div class="main-content"> +<?php +print $selected_page->getBodyWithImages(); +?> +</div> +<?php require(CLIENTINC_DIR.'footer.inc.php'); ?> diff --git a/scp/ajax.php b/scp/ajax.php index f6e2f36de5eb6a966eca31a4980cae32ea8406d9..54e2770736488081832ac325a9a345e94bb5081d 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -143,12 +143,6 @@ $dispatcher = patterns('', url_post('^(?P<tid>\d+)/lock$', 'acquireLock'), url_post('^(?P<tid>\d+)/lock/(?P<id>\d+)/renew', 'renewLock'), url_post('^(?P<tid>\d+)/lock/(?P<id>\d+)/release', 'releaseLock'), - url_get('^(?P<tid>\d+)/collaborators/preview$', 'previewCollaborators'), - url_get('^(?P<tid>\d+)/collaborators$', 'showCollaborators'), - url_post('^(?P<tid>\d+)/collaborators$', 'updateCollaborators'), - url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'), - url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'), - url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'), url_get('^(?P<tid>\d+)/forms/manage$', 'manageForms'), url_post('^(?P<tid>\d+)/forms/manage$', 'updateForms'), url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse'), @@ -189,9 +183,15 @@ $dispatcher = patterns('', url_get('^mass/(?P<action>[\w.]+)', 'massProcess'), url_post('^mass/(?P<action>[\w.]+)', 'massProcess') )), - url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI', - url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'), - url_post('^(?P<cid>\d+)$', 'updateCollaborator') + url('^/thread/', patterns('ajax.thread.php:ThreadAjaxAPI', + url_get('^(?P<tid>\d+)/collaborators/preview$', 'previewCollaborators'), + url_get('^(?P<tid>\d+)/collaborators$', 'showCollaborators'), + url_post('^(?P<tid>\d+)/collaborators$', 'updateCollaborators'), + url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'), + url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'), + url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'), + url_get('^(?P<tid>\d+)/collaborators/(?P<cid>\d+)/view$', 'viewCollaborator'), + url_post('^(?P<tid>\d+)/collaborators/(?P<cid>\d+)$', 'updateCollaborator') )), url('^/draft/', patterns('ajax.draft.php:DraftAjaxAPI', url_post('^(?P<id>\d+)$', 'updateDraft'), diff --git a/scp/css/scp.css b/scp/css/scp.css index 061e241a1d73d1275a7cee449000d559a186f617..4dd7cef3afd1c4cf0b48512b451375e6146e447f 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -28,10 +28,6 @@ div#header a { color:#E65524; } -.form_table a:hover { - text-decoration: underline; -} - .centered { text-align:center; } @@ -613,6 +609,10 @@ a.print { height:25px; } +input[type=search] { + font-size: 16px; +} +.search input[type=search], .search input[type=text] { height:23px; line-height:23px; @@ -927,6 +927,7 @@ ul.tabs { border-bottom:1px solid #aaa; background:#eef3f8; position: relative; + box-shadow: inset 0 -5px 10px -9px rgba(0,0,0,0.3); } #response_options ul.tabs { @@ -967,7 +968,13 @@ ul.tabs li.active { text-align: center; border-top:2px solid #81a9d7; bottom: 0; - box-shadow: 4px -1px 6px -3px rgba(0,0,0,0.3); + box-shadow: 4px -1px 6px -3px rgba(0,0,0,0.2); +} +ul.tabs li:not(.active) { + box-shadow: inset 0 -5px 10px -9px rgba(0,0,0,0.2); +} +ul.tabs.clean li.active { + background-color: white; } ul.tabs li a { @@ -978,6 +985,9 @@ ul.tabs li a { outline: none; padding: 5px 10px; } +ul.tabs li a:hover { + text-decoration: none; +} ul.tabs li.active a { font-weight: bold; @@ -997,6 +1007,7 @@ ul.tabs.vertical { padding-bottom: 40px; padding-top: 10px; background: transparent; + box-shadow: inset -5px 0 10px -9px rgba(0,0,0,0.3); } ul.tabs.vertical.left { float: left; @@ -1014,6 +1025,9 @@ ul.tabs.vertical li { right: 0; height: auto; } +ul.tabs.vertical li:not(.active) { + box-shadow: inset -5px 0 10px -9px rgba(0,0,0,0.3); +} ul.tabs.vertical li + li { margin-top: 5px; @@ -1551,7 +1565,7 @@ time { -webkit-box-sizing: content-box; } -.dialog.draggable h3:hover { +.dialog.draggable h3.drag-handle:hover { cursor: move; } @@ -1776,6 +1790,8 @@ div.selected-signature { overflow-y: hidden; font-size: 15px; line-height: 1.25rem; + background-color: white; + background-color: rgba(255, 255, 255, 0.9); } div.selected-signature .inner { opacity: 0.5; @@ -2149,6 +2165,7 @@ td.indented { } .accordian dt.active a { color: #184E81; + text-decoration: none; } .accordian dt:not(.active) a i { display: none; diff --git a/scp/css/tooltip.css b/scp/css/tooltip.css new file mode 100644 index 0000000000000000000000000000000000000000..ae54e90fc6b05e52e73828a6a4fa8492244023bc --- /dev/null +++ b/scp/css/tooltip.css @@ -0,0 +1,115 @@ +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.428571429; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + background-color: #000000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} + diff --git a/scp/emailtest.php b/scp/emailtest.php index 9e15baf36b1ccb10d0cd3cc93e87b94edf9d88c3..cd9fbae9d67afcab14e96ed180d5490faab6bae9 100644 --- a/scp/emailtest.php +++ b/scp/emailtest.php @@ -99,7 +99,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('To');?>: </td> <td> - <input type="text" size="60" name="email" value="<?php echo $info['email']; ?>"> + <input type="text" size="60" name="email" value="<?php echo $info['email']; ?>" + autofocus> <span class="error">* <?php echo $errors['email']; ?></span> </td> </tr> diff --git a/scp/js/bootstrap-tooltip.js b/scp/js/bootstrap-tooltip.js new file mode 100644 index 0000000000000000000000000000000000000000..0779f139d6ccd8349f5ac1a2a8610a4a2515047e --- /dev/null +++ b/scp/js/bootstrap-tooltip.js @@ -0,0 +1,514 @@ +/* ======================================================================== + * Bootstrap: tooltip.js v3.3.4 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TOOLTIP PUBLIC CLASS DEFINITION + // =============================== + + var Tooltip = function (element, options) { + this.type = null + this.options = null + this.enabled = null + this.timeout = null + this.hoverState = null + this.$element = null + this.inState = null + + this.init('tooltip', element, options) + } + + Tooltip.VERSION = '3.3.4' + + Tooltip.TRANSITION_DURATION = 150 + + Tooltip.DEFAULTS = { + animation: true, + placement: 'top', + selector: false, + template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + container: false, + viewport: { + selector: 'body', + padding: 0 + } + } + + Tooltip.prototype.init = function (type, element, options) { + this.enabled = true + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) + this.inState = { click: false, hover: false, focus: false } + + if (this.$element[0] instanceof document.constructor && !this.options.selector) { + throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') + } + + var triggers = this.options.trigger.split(' ') + + for (var i = triggers.length; i--;) { + var trigger = triggers[i] + + if (trigger == 'click') { + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) + } else if (trigger != 'manual') { + var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' + var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' + + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) + } + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + Tooltip.prototype.getDefaults = function () { + return Tooltip.DEFAULTS + } + + Tooltip.prototype.getOptions = function (options) { + options = $.extend({}, this.getDefaults(), this.$element.data(), options) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay, + hide: options.delay + } + } + + return options + } + + Tooltip.prototype.getDelegateOptions = function () { + var options = {} + var defaults = this.getDefaults() + + this._options && $.each(this._options, function (key, value) { + if (defaults[key] != value) options[key] = value + }) + + return options + } + + Tooltip.prototype.enter = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true + } + + if (self.tip().hasClass('in') || self.hoverState == 'in') { + self.hoverState = 'in' + return + } + + clearTimeout(self.timeout) + + self.hoverState = 'in' + + if (!self.options.delay || !self.options.delay.show) return self.show() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'in') self.show() + }, self.options.delay.show) + } + + Tooltip.prototype.isInStateTrue = function () { + for (var key in this.inState) { + if (this.inState[key]) return true + } + + return false + } + + Tooltip.prototype.leave = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false + } + + if (self.isInStateTrue()) return + + clearTimeout(self.timeout) + + self.hoverState = 'out' + + if (!self.options.delay || !self.options.delay.hide) return self.hide() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'out') self.hide() + }, self.options.delay.hide) + } + + Tooltip.prototype.show = function () { + var e = $.Event('show.bs.' + this.type) + + if (this.hasContent() && this.enabled) { + this.$element.trigger(e) + + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) + if (e.isDefaultPrevented() || !inDom) return + var that = this + + var $tip = this.tip() + + var tipId = this.getUID(this.type) + + this.setContent() + $tip.attr('id', tipId) + this.$element.attr('aria-describedby', tipId) + + if (this.options.animation) $tip.addClass('fade') + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + var autoToken = /\s?auto?\s?/i + var autoPlace = autoToken.test(placement) + if (autoPlace) placement = placement.replace(autoToken, '') || 'top' + + $tip + .detach() + .css({ top: 0, left: 0, display: 'block' }) + .addClass(placement) + .data('bs.' + this.type, this) + + this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) + this.$element.trigger('inserted.bs.' + this.type) + + var pos = this.getPosition() + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (autoPlace) { + var orgPlacement = placement + var viewportDim = this.getPosition(this.$viewport) + + placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : + placement + + $tip + .removeClass(orgPlacement) + .addClass(placement) + } + + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) + + this.applyPlacement(calculatedOffset, placement) + + var complete = function () { + var prevHoverState = that.hoverState + that.$element.trigger('shown.bs.' + that.type) + that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) + } + + $.support.transition && this.$tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + } + } + + Tooltip.prototype.applyPlacement = function (offset, placement) { + var $tip = this.tip() + var width = $tip[0].offsetWidth + var height = $tip[0].offsetHeight + + // manually read margins because getBoundingClientRect includes difference + var marginTop = parseInt($tip.css('margin-top'), 10) + var marginLeft = parseInt($tip.css('margin-left'), 10) + + // we must check for NaN for ie 8/9 + if (isNaN(marginTop)) marginTop = 0 + if (isNaN(marginLeft)) marginLeft = 0 + + offset.top += marginTop + offset.left += marginLeft + + // $.fn.offset doesn't round pixel values + // so we use setOffset directly with our own function B-0 + $.offset.setOffset($tip[0], $.extend({ + using: function (props) { + $tip.css({ + top: Math.round(props.top), + left: Math.round(props.left) + }) + } + }, offset), 0) + + $tip.addClass('in') + + // check to see if placing tip in new offset caused the tip to resize itself + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (placement == 'top' && actualHeight != height) { + offset.top = offset.top + height - actualHeight + } + + var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) + + if (delta.left) offset.left += delta.left + else offset.top += delta.top + + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' + + $tip.offset(offset) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) + } + + Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { + this.arrow() + .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isVertical ? 'top' : 'left', '') + } + + Tooltip.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + + $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) + $tip.removeClass('fade in top bottom left right') + } + + Tooltip.prototype.hide = function (callback) { + var that = this + var $tip = $(this.$tip) + var e = $.Event('hide.bs.' + this.type) + + function complete() { + if (that.hoverState != 'in') $tip.detach() + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + callback && callback() + } + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + $tip.removeClass('in') + + $.support.transition && $tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + + this.hoverState = null + + return this + } + + Tooltip.prototype.fixTitle = function () { + var $e = this.$element + if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') + } + } + + Tooltip.prototype.hasContent = function () { + return this.getTitle() + } + + Tooltip.prototype.getPosition = function ($element) { + $element = $element || this.$element + + var el = $element[0] + var isBody = el.tagName == 'BODY' + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) + } + + Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { + return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : + /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + + } + + Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { + var delta = { top: 0, left: 0 } + if (!this.$viewport) return delta + + var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 + var viewportDimensions = this.getPosition(this.$viewport) + + if (/right|left/.test(placement)) { + var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll + var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight + if (topEdgeOffset < viewportDimensions.top) { // top overflow + delta.top = viewportDimensions.top - topEdgeOffset + } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow + delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset + } + } else { + var leftEdgeOffset = pos.left - viewportPadding + var rightEdgeOffset = pos.left + viewportPadding + actualWidth + if (leftEdgeOffset < viewportDimensions.left) { // left overflow + delta.left = viewportDimensions.left - leftEdgeOffset + } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow + delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset + } + } + + return delta + } + + Tooltip.prototype.getTitle = function () { + var title + var $e = this.$element + var o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + return title + } + + Tooltip.prototype.getUID = function (prefix) { + do prefix += ~~(Math.random() * 1000000) + while (document.getElementById(prefix)) + return prefix + } + + Tooltip.prototype.tip = function () { + if (!this.$tip) { + this.$tip = $(this.options.template) + if (this.$tip.length != 1) { + throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') + } + } + return this.$tip + } + + Tooltip.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) + } + + Tooltip.prototype.enable = function () { + this.enabled = true + } + + Tooltip.prototype.disable = function () { + this.enabled = false + } + + Tooltip.prototype.toggleEnabled = function () { + this.enabled = !this.enabled + } + + Tooltip.prototype.toggle = function (e) { + var self = this + if (e) { + self = $(e.currentTarget).data('bs.' + this.type) + if (!self) { + self = new this.constructor(e.currentTarget, this.getDelegateOptions()) + $(e.currentTarget).data('bs.' + this.type, self) + } + } + + if (e) { + self.inState.click = !self.inState.click + if (self.isInStateTrue()) self.enter(self) + else self.leave(self) + } else { + self.tip().hasClass('in') ? self.leave(self) : self.enter(self) + } + } + + Tooltip.prototype.destroy = function () { + var that = this + clearTimeout(this.timeout) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + if (that.$tip) { + that.$tip.detach() + } + that.$tip = null + that.$arrow = null + that.$viewport = null + }) + } + + + // TOOLTIP PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tooltip + + $.fn.tooltip = Plugin + $.fn.tooltip.Constructor = Tooltip + + + // TOOLTIP NO CONFLICT + // =================== + + $.fn.tooltip.noConflict = function () { + $.fn.tooltip = old + return this + } + +}(jQuery); diff --git a/scp/js/bootstrap-typeahead.js b/scp/js/bootstrap-typeahead.js index 6c8238ad132a24178d565420083d6ccd0cd27cff..7ddc01f61c56312e657d858e286c0b3c5a03401a 100644 --- a/scp/js/bootstrap-typeahead.js +++ b/scp/js/bootstrap-typeahead.js @@ -41,7 +41,7 @@ constructor: Typeahead - , select: function () { + , select: function (e) { var val = JSON.parse(this.$menu.find('.active').attr('data-value')) , text @@ -51,7 +51,8 @@ this.$element.val(text) if (typeof this.onselect == "function") - this.onselect(val) + if (false === this.onselect(val, e)) + return; return this.hide() } @@ -252,7 +253,7 @@ case 9: // tab case 13: // enter if (!this.shown) return - this.select() + this.select(e) break case 27: // escape @@ -298,7 +299,7 @@ , click: function (e) { e.stopPropagation() e.preventDefault() - this.select() + this.select(e) } , mouseenter: function (e) { diff --git a/scp/js/jquery.translatable.js b/scp/js/jquery.translatable.js index 08a28ae3c7e030fdf20c79a6882a6dfd55974019..08f754a1a5e85c0d50f9759edb458b1a2e7e8a7e 100644 --- a/scp/js/jquery.translatable.js +++ b/scp/js/jquery.translatable.js @@ -60,6 +60,7 @@ .focus($.proxy(function() { this.addClass('focus'); }, this.$container)) .blur($.proxy(function() { this.removeClass('focus'); }, this.$container)); getConfig().then($.proxy(function(c) { + this.attr({'spellcheck': 'true', 'lang': c.primary_language}) $('<span class="flag"></span>') .addClass('flag-' + c.primary_lang_flag) .insertAfter(this); @@ -113,6 +114,8 @@ .text(info.name) .prepend($('<span>').addClass('flag flag-'+info.flag)) .append($('<input type="text" data-lang="'+lang+'">') + .attr('lang', lang) + .attr('spellcheck', 'true') .attr('dir', info.direction || 'ltr') .on('change keydown', $.proxy(this.showCommit, this)) .val(text) diff --git a/scp/js/scp.js b/scp/js/scp.js index 7bfaf9a910b2cf4fb89496389b3412b475376a31..d1c73fc8bf47f33a938b5194803683a234a90534 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -37,7 +37,10 @@ function checkbox_checker(formObj, min, max) { var scp_prep = function() { - $("input:not(.dp):visible:enabled:first").focus(); + $("input[autofocus]:visible:enabled:first").each(function() { + if ($(this).val()) + $(this).blur(); + }); $('table.list input:checkbox').bind('click, change', function() { $(this) .parents("tr:first") @@ -314,14 +317,14 @@ var scp_prep = function() { top : (w.innerHeight() / 7), left : (w.width() - $this.outerWidth()) / 2 }); - $this.hasClass('draggable') && $this.draggable({handle:'h3'}); + $this.hasClass('draggable') && $this.draggable({handle:'.drag-handle'}); }); $('.dialog').each(function() { $this=$(this); $this.resize(); - $this.hasClass('draggable') && $this.draggable({handle:'h3'}); + $this.hasClass('draggable') && $this.draggable({handle:'.drag-handle'}); }); $('.dialog').delegate('input.close, a.close', 'click', function(e) { @@ -447,13 +450,13 @@ var scp_prep = function() { stop = $('div.sticky.bar.stop'), stopAt, visible = false; - if (stop.length) - stopAt = stop.offset().top - $that.height(); $that.find('.content').width($that.width()); $(window).scroll(function (event) { // what the y position of the scroll is var y = $(this).scrollTop(); + if (stop.length) + stopAt = stop.offset().top - $that.height(); // whether that's below the form if (y >= top && (!stopAt || stopAt > y)) { @@ -476,6 +479,8 @@ var scp_prep = function() { } }); }); + + $('[data-toggle="tooltip"]').tooltip() }; $(document).ready(scp_prep); @@ -563,6 +568,16 @@ $(document).keydown(function(e) { } }); + +$(document).on('focus', 'form.spellcheck textarea, form.spellcheck input[type=text]', function() { + var $this = $(this); + if ($this.attr('lang') !== undefined) + return; + var lang = $(this).closest('[lang]').attr('lang'); + if (lang) + $(this).attr({'spellcheck':'true', 'lang': lang}); +}); + $.toggleOverlay = function (show) { if (typeof(show) === 'undefined') { return $.toggleOverlay(!$('#overlay').is(':visible')); @@ -663,7 +678,7 @@ $.sysAlert = function (title, msg, cb) { $.toggleOverlay(true); $('#title', $dialog).html(title); $('#body', $dialog).html(msg); - $dialog.show(); + $dialog.resize().show(); if (cb) $dialog.find('input.ok.close').click(cb); } else { @@ -700,7 +715,7 @@ $.confirm = function(message, title) { .click(function() { hide(); D.resolve(); }) ))).append($('<div class="clear"></div>')); $.toggleOverlay(true); - $popup.show(); + $popup.resize().show(); return D.promise(); }; @@ -787,6 +802,11 @@ $.changeHash = function(hash, quiet) { } }; +// Forms — submit, stay on same tab +$(document).on('submit', 'form', function() { + $(this).attr('action', $(this).attr('action') + window.location.hash); +}); + //Collaborators $(document).on('click', 'a.collaborator, a.collaborators', function(e) { e.preventDefault(); @@ -804,21 +824,10 @@ $(document).on('click', 'a.collaborator, a.collaborators', function(e) { // NOTE: getConfig should be global getConfig = (function() { var dfd = $.Deferred(), - requested = null; + requested = false; return function() { - if (dfd.state() != 'resolved' && !requested) - requested = $.ajax({ - url: "ajax.php/config/scp", - dataType: 'json', - success: function (json_config) { - dfd.resolve(json_config); - }, - error: function() { - requested = null; - } - }); return dfd; - } + }; })(); $(document).on('pjax:click', function(options) { @@ -834,7 +843,7 @@ $(document).on('pjax:click', function(options) { if ($(this).data('timer')) clearTimeout($(this).data('timer')); }); - $('.tip_box').remove(); + $('.tip_box, .typeahead.dropdown-menu').remove(); }); $(document).on('pjax:start', function() { @@ -872,6 +881,17 @@ $(document).on('pjax:complete', function() { $('#overlay').removeAttr('style'); }); +// Enable PJAX for the staff interface +if ($.support.pjax) { + $(document).on('click', 'a', function(event) { + var $this = $(this); + if (!$this.hasClass('no-pjax') + && !$this.closest('.no-pjax').length + && $this.attr('href')[0] != '#') + $.pjax.click(event, {container: $this.data('pjaxContainer') || $('#pjax-container'), timeout: 2000}); + }) +} + // Quick note interface $(document).on('click.note', '.quicknote .action.edit-note', function() { var note = $(this).closest('.quicknote'), diff --git a/scp/js/ticket.js b/scp/js/ticket.js index a105918683214496cb4050d7d238089924dc120b..9e731a756fccbb9bb3e889833901ad456a406659 100644 --- a/scp/js/ticket.js +++ b/scp/js/ticket.js @@ -328,7 +328,9 @@ $.showImagesInline = function(urls, thread_id) { } ).append($('<div class="caption">') .append('<span class="filename">'+info.filename+'</span>') - .append('<a href="'+info.download_url+'" class="action-button pull-right no-pjax"><i class="icon-download-alt"></i> '+__('Download')+'</a>') + .append($('<a href="'+info.download_url+'" class="action-button pull-right no-pjax"><i class="icon-download-alt"></i> '+__('Download')+'</a>') + .attr('download', info.filename) + ) ); e.data('wrapped', true); } @@ -341,6 +343,9 @@ $.refreshTicketView = function() { } var ticket_onload = function($) { + if (0 === $('#ticket_thread').length) + return; + //Start watching the form for activity. autoLock.Init(); @@ -419,11 +424,12 @@ var ticket_onload = function($) { }); }); - $('.thread-body').each(function() { - var urls = $(this).data('urls'); - if (urls) - $.showImagesInline(urls, $(this).data('id')); - }); + $.showImagesInline($('#ticket_thread').data('imageUrls')); + + var last_entry = $('#ticket_thread .thread-entry').last().offset().top - 50; + $('html, body').animate({ + scrollTop: last_entry + }, 1000); }; $(ticket_onload); $(document).on('pjax:success', function() { ticket_onload(jQuery); }); diff --git a/scp/js/tips.js b/scp/js/tips.js index 76fabcd7bbc4814f7cc7db6c801daee1ed8a1b68..5801829bd10ec9f257dd47fb9030e8bdcf834393 100644 --- a/scp/js/tips.js +++ b/scp/js/tips.js @@ -53,8 +53,9 @@ jQuery(function() { }, getHelpTips = (function() { var dfd, cache = {}; - return function(namespace) { - var namespace = namespace + return function(elem) { + var namespace = + $(elem).closest('[data-tip-namespace]').data('tipNamespace') || $('#content').data('tipNamespace') || $('meta[name=tip-namespace]').attr('content'); if (!namespace) @@ -147,7 +148,7 @@ jQuery(function() { clearTimeout(tip_timer); }); - getHelpTips().then(function(tips) { + getHelpTips(elem).then(function(tips) { var href = elem.attr('href'); if (href) { section = tips[elem.attr('href').substr(1)]; diff --git a/setup/js/tips.js b/setup/js/tips.js index 1e35d976897cfc3cd121f3a8cb0ad195eb55fecf..a3e8b06463a22dfa94e8a00c85d7ef3df2c422a5 100644 --- a/setup/js/tips.js +++ b/setup/js/tips.js @@ -18,7 +18,7 @@ jQuery(function($) { .each(function(i, e) { e.rel = 'tip-' + i; }) - .live('mouseover click', function(e) { + .on('mouseover click', function(e) { e.preventDefault(); var elem = $(this), @@ -51,7 +51,7 @@ jQuery(function($) { } }, 500); - elem.live('mouseout', function() { + elem.on('mouseout', function() { clearTimeout(tip_timer); }); diff --git a/setup/test/run-tests.php b/setup/test/run-tests.php index f42c121d7e48b345bf6407b2cde0ea25dc2454c3..30d81e225735c50e21d2634cae790d00b22d1d4f 100644 --- a/setup/test/run-tests.php +++ b/setup/test/run-tests.php @@ -8,6 +8,7 @@ $selected_test = (isset($argv[1])) ? $argv[1] : false; require_once 'bootstrap.php'; require_once "tests/class.test.php"; +$root = get_osticket_root_path(); $fails = array(); require_once INCLUDE_DIR . 'class.i18n.php'; diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php index 620f763a55090b6bb423e2b36a6b378f44255a8e..24670dbdea13641aa632e59d82231af895c4c611 100644 --- a/setup/test/tests/stubs.php +++ b/setup/test/tests/stubs.php @@ -37,6 +37,7 @@ class mysqli_result { class ReflectionClass { function getMethods() {} function getConstants() {} + function newInstanceArgs() {} } class DomNode { diff --git a/tickets.php b/tickets.php index 10f70676a4713d3c7b696f8304a2c6bf74067a70..0c6dd8947c1925f025552e20971b8f8cc0079c82 100644 --- a/tickets.php +++ b/tickets.php @@ -35,7 +35,7 @@ if($_REQUEST['id']) { if (!$ticket && $thisclient->isGuest()) Http::redirect('view.php'); -$tform = TicketForm::objects()->one(); +$tform = TicketForm::objects()->one()->getForm(); $messageField = $tform->getField('message'); $attachments = $messageField->getWidget()->getAttachments(); @@ -130,8 +130,6 @@ if($ticket && $ticket->checkUserAccess($thisclient)) { } include(CLIENTINC_DIR.'header.inc.php'); include(CLIENTINC_DIR.$inc); -if ($tform instanceof DynamicFormEntry) - $tform = $tform->getForm(); print $tform->getMedia(); include(CLIENTINC_DIR.'footer.inc.php'); ?>