Big checkin that adds a number of features and makes some changes:

* Updated jQuery to 1.2.6
* Add jQuery UI 1.6b for interface effects as needed
    * 'Smoothness' theme from ThemeRoller.com added.
* Clean up 'Filter' dialog on Ticket List, long way to go still.
    * Uses tabs to save a query or load a saved query
    * Lots of misuse of space here, can be cleaned up somewhat still.
* Add ability for users to save filters/queries
    * Saved queries can be shared, so other users can use them
    * Users can run saved queries instead of re-filtering
    * Filter mechanism in Ticket List had to be reworked significantly
* Merged 3rd party licenses into LICENSE.3RDPARTY
* Updated messages files for EN locale

To update, ensure you run './manage.py syncdb' to add the SavedSearch
table.
This commit is contained in:
Ross Poulton 2008-08-28 09:06:24 +00:00
parent 5f9b85fbbf
commit 0068eccbf4
61 changed files with 1593 additions and 234 deletions

View File

@ -28,6 +28,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
EXCEPTIONS
This software is distributed with a copy of jQuery, a javascript library written by John Resig. jQuery is not distributed under the above license, but rather uses the MIT license. See LICENSE.JQUERY for further details.
This software is also distributed with a copy of nicEdit, a javascript rich-text editor written by Brian Kirchoff. nicEdit is not distributed under the above license, but rather uses the MIT license. See LICENSE.NICEDIT for further details.
This software is distributed with some third-party software which is not distributed under the above license. See LICENSE.3RDPARTY for further details.

69
LICENSE.3RDPARTY Normal file
View File

@ -0,0 +1,69 @@
This file contains license details for 3rd party software which is
distributed with Jutda Helpdesk.
1. License for jQuery & jQuery UI
2. License for nicEdit
3. License for jQuery UI 'Smoothness' theme
----------------------------------------------------------------------
1. License for jQuery & jQuery UI
Copyright (c) 2007 John Resig, http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------
2. License for nicEdit
Copyright (c) 2007 Brian Kirchoff (http://nicedit.com)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------
3. License for jQuery UI 'Smoothness' theme
/*
* jQuery UI screen structure and presentation
* This CSS file was generated by ThemeRoller, a Filament Group Project for jQuery UI
* Author: Scott Jehl, scott@filamentgroup.com, http://www.filamentgroup.com
* Visit ThemeRoller.com
*/

View File

@ -1,20 +0,0 @@
Copyright (c) 2007 John Resig, http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,22 +0,0 @@
Copyright (c) 2007 Brian Kirchoff (http://nicedit.com)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -20,9 +20,6 @@ class AttachmentInline(admin.StackedInline):
class FollowUpAdmin(admin.ModelAdmin):
inlines = [TicketChangeInline, AttachmentInline]
class PreSetReplyAdmin(admin.ModelAdmin):
list_display = ('name',)
admin.site.register(Ticket, TicketAdmin)
admin.site.register(Queue, QueueAdmin)
admin.site.register(FollowUp, FollowUpAdmin)

View File

@ -200,7 +200,24 @@ textarea#commentBox {
.filterBox {
display: none;
float: left;
border: solid #ccc 1px;
padding: 2px;
margin: 4px;
max-width: 24%;
}
.filterBoxShow {
display: block;
}
.filterBox label {
clear: both;
display: block;
}
.filterBox .filterHelp {
color: #aaa;
font-size: 0.8em;
clear: both;
}

View File

@ -0,0 +1,167 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Themeroller Demo</title>
<script src="http://ui.jquery.com/js/jquery.js"></script>
<script src="http://ui.jquery.com/js/ui.js"></script>
<link rel="stylesheet" href="http://ui.jquery.com/applications/themeroller/css/app_screen.css" type="text/css" media="screen">
<link rel="stylesheet" href="jquery-ui-themeroller.css" type="text/css" media="screen">
<script type="text/javascript">
$(function(){
//accordion
$('#accordion').accordion({
header: ".ui-accordion-header",
clearStyle: true
});
//tabs
$('#tabs ul').tabs();
//datepicker
$('#ui-datepicker').datepicker({
changeFirstDay: false
});
//slider
$('#slider').slider({range: true});
//dialog (currently not js-driven in themeroller)
/*
var dOffset = $('#dialogContent').offset();
$('#dialogContent').dialog({
buttons: {
'Okay': function() { },
'Cancel': function() { }
}
});
window.scrollTo(0,0);
*/
});
</script>
</head>
<body>
<div id="compGroupA" class="clearfix">
<!-- ACCORDION -->
<h2 class="demoHeaders">Accordion</h2>
<div id="accordion">
<div class="ui-accordion-group">
<h3 class="ui-accordion-header"><a href="#">Test 1</a></h3>
<div class="ui-accordion-content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</div>
</div>
<div class="ui-accordion-group">
<h3 class="ui-accordion-header"><a href="#">Test 2</a></h3>
<div class="ui-accordion-content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> </div>
</div>
<div class="ui-accordion-group">
<h3 class="ui-accordion-header"><a href="#">Test 3</a></h3>
<div class="ui-accordion-content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
</div>
<!-- TABS -->
<h2 class="demoHeaders">Tabs</h2>
<div id="tabs">
<ul>
<li class="ui-tabs-nav-item"><a href="#fragment-1">First Section</a></li>
<li class="ui-tabs-nav-item"><a href="#fragment-2">Second Section</a></li>
<li class="ui-tabs-nav-item"><a href="#fragment-3"><span>Third Section</span></a></li>
</ul>
<div id="fragment-1">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</div>
<div id="fragment-2">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div id="fragment-3">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
</div><!-- /#compGroupA -->
<div id="compGroupB" class="clearfix">
<!-- SLIDER -->
<h2 class="demoHeaders">Slider</h2>
<div id="slider">
<div class='ui-slider-handle'></div>
<div class='ui-slider-handle' style="left: 100px;"></div>
</div>
<!-- Date Picker -->
<h2 class="demoHeaders">Date Picker</h2>
<div id="ui-datepicker" class="clearfix"></div>
<!-- Dialog NOTE: Dialog is not generated by UI in this demo so it can be visually styled in themeroller-->
<h2 class="demoHeaders">Dialog</h2>
<div class="ui-dialog ui-draggable" style="width: 30em; height: 20em; ">
<div style="position: relative;" class="ui-dialog-container">
<div class="ui-dialog-titlebar">
<span class="ui-dialog-title">This is my title</span>
<a href="#" class="ui-dialog-titlebar-close"><span>X</span></a>
</div>
<div class="ui-dialog-content" id="dialogContent" title="This is my title">
<p>I'm in a dialog!</p>
</div>
</div>
<div class="ui-resizable-n ui-resizable-handle"></div>
<div class="ui-resizable-s ui-resizable-handle"></div>
<div class="ui-resizable-e ui-resizable-handle"></div>
<div class="ui-resizable-w ui-resizable-handle"></div>
<div class="ui-resizable-ne ui-resizable-handle"></div>
<div class="ui-resizable-se ui-resizable-handle"></div>
<div class="ui-resizable-sw ui-resizable-handle"></div>
<div class="ui-resizable-nw ui-resizable-handle"></div>
<div class="ui-dialog-buttonpane">
<button>Okay</button>
<button>Cancel</button>
</div>
</div>
</div><!-- /#compGroupB -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

View File

@ -0,0 +1,832 @@
/*
* jQuery UI screen structure and presentation
* This CSS file was generated by ThemeRoller, a Filament Group Project for jQuery UI
* Author: Scott Jehl, scott@filamentgroup.com, http://www.filamentgroup.com
* Visit ThemeRoller.com
*/
/*
* Note: If your ThemeRoller settings have a font size set in ems, your components will scale according to their parent element's font size.
* As a rule of thumb, set your body's font size to 62.5% to make 1em = 10px.
* body {font-size: 62.5%;}
*/
/*UI accordion*/
.ui-accordion {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
border-bottom: 1px solid #d3d3d3;
}
.ui-accordion-group {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
border: 1px solid #d3d3d3;
border-bottom: none;
}
.ui-accordion-header {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
cursor: pointer;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
}
.ui-accordion-header a {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
display: block;
font-size: 1em;
font-weight: normal;
text-decoration: none;
padding: .5em .5em .5em 1.7em;
color: #555555;
background: url(images/888888_7x7_arrow_right.gif) .5em 50% no-repeat;
}
.ui-accordion-header a:hover {
background: url(images/454545_7x7_arrow_right.gif) .5em 50% no-repeat;
color: #212121;
}
.ui-accordion-header:hover {
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
color: #212121;
}
.selected .ui-accordion-header, .selected .ui-accordion-header:hover {
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
}
.selected .ui-accordion-header a, .selected .ui-accordion-header a:hover {
color: #222222;
background: url(images/222222_7x7_arrow_down.gif) .5em 50% no-repeat;
}
.ui-accordion-content {
padding: 1.5em 1.7em;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
color: #222222;
font-size: 1em;
}
/*UI tabs*/
.ui-tabs-nav {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
float: left;
position: relative;
z-index: 1;
border-right: 1px solid #d3d3d3;
bottom: -1px;
}
.ui-tabs-nav-item {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
float: left;
border: 1px solid #d3d3d3;
border-right: none;
}
.ui-tabs-nav-item a {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
float: left;
font-size: 1em;
font-weight: normal;
text-decoration: none;
padding: .5em 1.7em;
color: #555555;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
}
.ui-tabs-nav-item a:hover {
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
color: #212121;
}
.ui-tabs-selected {
border-bottom-color: #ffffff;
}
.ui-tabs-selected a, .ui-tabs-selected a:hover {
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
color: #222222;
}
.ui-tabs-panel {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
clear:left;
border: 1px solid #d3d3d3;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
color: #222222;
padding: 1.5em 1.7em;
font-size: 1.1em;
}
.ui-tabs-hide {
display: none;/* for accessible hiding: position: absolute; left: -99999999px*/;
}
/*slider*/
.ui-slider {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
border: 1px solid #dddddd;
height: .8em;
position: relative;
}
.ui-slider-handle {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
position: absolute;
z-index: 2;
top: -3px;
width: 1.2em;
height: 1.2em;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
border: 1px solid #d3d3d3;
}
.ui-slider-handle:hover {
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
border: 1px solid #999999;
}
.ui-slider-handle-active, .ui-slider-handle-active:hover {
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
border: 1px solid #dddddd;
}
.ui-slider-range {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
height: .8em;
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
position: absolute;
border: 1px solid #d3d3d3;
border-left: 0;
border-right: 0;
top: -1px;
z-index: 1;
opacity:.7;
filter:Alpha(Opacity=70);
}
/*dialog*/
.ui-dialog {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
color: #222222;
border: 4px solid #dddddd;
position: relative;
}
.ui-resizable-handle {
position: absolute;
font-size: 0.1px;
z-index: 99999;
}
.ui-resizable .ui-resizable-handle {
display: block;
}
body .ui-resizable-disabled .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
body .ui-resizable-autohide .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
.ui-resizable-n {
cursor: n-resize;
height: 7px;
width: 100%;
top: -5px;
left: 0px;
}
.ui-resizable-s {
cursor: s-resize;
height: 7px;
width: 100%;
bottom: -5px;
left: 0px;
}
.ui-resizable-e {
cursor: e-resize;
width: 7px;
right: -5px;
top: 0px;
height: 100%;
}
.ui-resizable-w {
cursor: w-resize;
width: 7px;
left: -5px;
top: 0px;
height: 100%;
}
.ui-resizable-se {
cursor: se-resize;
width: 13px;
height: 13px;
right: 0px;
bottom: 0px;
background: url(images/222222_11x11_icon_resize_se.gif) no-repeat 0 0;
}
.ui-resizable-sw {
cursor: sw-resize;
width: 9px;
height: 9px;
left: 0px;
bottom: 0px;
}
.ui-resizable-nw {
cursor: nw-resize;
width: 9px;
height: 9px;
left: 0px;
top: 0px;
}
.ui-resizable-ne {
cursor: ne-resize;
width: 9px;
height: 9px;
right: 0px;
top: 0px;
}
.ui-dialog-titlebar {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
padding: .5em 1.5em .5em 1em;
color: #555555;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
border-bottom: 1px solid #d3d3d3;
font-size: 1em;
font-weight: normal;
position: relative;
}
.ui-dialog-title {}
.ui-dialog-titlebar-close {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
background: url(images/888888_11x11_icon_close.gif) 0 0 no-repeat;
position: absolute;
right: 8px;
top: .7em;
width: 11px;
height: 11px;
z-index: 100;
}
.ui-dialog-titlebar-close-hover, .ui-dialog-titlebar-close:hover {
background: url(images/454545_11x11_icon_close.gif) 0 0 no-repeat;
}
.ui-dialog-titlebar-close:active {
background: url(images/222222_11x11_icon_close.gif) 0 0 no-repeat;
}
.ui-dialog-titlebar-close span {
display: none;
}
.ui-dialog-content {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
color: #222222;
padding: 1.5em 1.7em;
}
.ui-dialog-buttonpane {
position: absolute;
bottom: 0;
width: 100%;
text-align: left;
border-top: 1px solid #dddddd;
background: #ffffff;
}
.ui-dialog-buttonpane button {
margin: .5em 0 .5em 8px;
color: #555555;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
font-size: 1em;
border: 1px solid #d3d3d3;
cursor: pointer;
padding: .2em .6em .3em .6em;
line-height: 1.4em;
}
.ui-dialog-buttonpane button:hover {
color: #212121;
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
border: 1px solid #999999;
}
.ui-dialog-buttonpane button:active {
color: #222222;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
border: 1px solid #dddddd;
}
/* This file skins dialog */
.ui-dialog.ui-draggable .ui-dialog-titlebar,
.ui-dialog.ui-draggable .ui-dialog-titlebar {
cursor: move;
}
/*datepicker*/
/* Main Style Sheet for jQuery UI date picker */
.ui-datepicker-div, .ui-datepicker-inline, #ui-datepicker-div {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
font-size: 1.1em;
border: 4px solid #dddddd;
width: 15.5em;
padding: 2.5em .5em .5em .5em;
position: relative;
}
.ui-datepicker-div, #ui-datepicker-div {
z-index: 9999; /*must have*/
display: none;
}
.ui-datepicker-inline {
float: left;
display: block;
}
.ui-datepicker-control {
display: none;
}
.ui-datepicker-current {
display: none;
}
.ui-datepicker-next, .ui-datepicker-prev {
position: absolute;
left: .5em;
top: .5em;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
}
.ui-datepicker-next {
left: 14.6em;
}
.ui-datepicker-next:hover, .ui-datepicker-prev:hover {
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
}
.ui-datepicker-next a, .ui-datepicker-prev a {
text-indent: -999999px;
width: 1.3em;
height: 1.4em;
display: block;
font-size: 1em;
background: url(images/888888_7x7_arrow_left.gif) 50% 50% no-repeat;
border: 1px solid #d3d3d3;
cursor: pointer;
}
.ui-datepicker-next a {
background: url(images/888888_7x7_arrow_right.gif) 50% 50% no-repeat;
}
.ui-datepicker-prev a:hover {
background: url(images/454545_7x7_arrow_left.gif) 50% 50% no-repeat;
}
.ui-datepicker-next a:hover {
background: url(images/454545_7x7_arrow_right.gif) 50% 50% no-repeat;
}
.ui-datepicker-prev a:active {
background: url(images/222222_7x7_arrow_left.gif) 50% 50% no-repeat;
}
.ui-datepicker-next a:active {
background: url(images/222222_7x7_arrow_right.gif) 50% 50% no-repeat;
}
.ui-datepicker-header select {
border: 1px solid #d3d3d3;
color: #555555;
background: #e6e6e6;
font-size: 1em;
line-height: 1.4em;
position: absolute;
top: .5em;
margin: 0 !important;
}
.ui-datepicker-header option:focus, .ui-datepicker-header option:hover {
background: #dadada;
}
.ui-datepicker-header select.ui-datepicker-new-month {
width: 7em;
left: 2.2em;
}
.ui-datepicker-header select.ui-datepicker-new-year {
width: 5em;
left: 9.4em;
}
table.ui-datepicker {
width: 15.5em;
text-align: right;
}
table.ui-datepicker td a {
padding: .1em .3em .1em 0;
display: block;
color: #555555;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
cursor: pointer;
border: 1px solid #ffffff;
}
table.ui-datepicker td a:hover {
border: 1px solid #999999;
color: #212121;
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
}
table.ui-datepicker td a:active {
border: 1px solid #dddddd;
color: #222222;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
}
table.ui-datepicker .ui-datepicker-title-row td {
padding: .3em 0;
text-align: center;
font-size: .9em;
color: #222222;
text-transform: uppercase;
}
table.ui-datepicker .ui-datepicker-title-row td a {
color: #222222;
}
.ui-datepicker-cover {
display: none;
display/**/: block;
position: absolute;
z-index: -1;
filter: mask();
top: -4px;
left: -4px;
width: 193px;
height: 200px;
}
/* ui-autocomplete */
.ui-autocomplete-results {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
z-index: 9999;
}
.ui-autocomplete-results ul, .ui-autocomplete-results li {
margin: 0;
padding: 0;
list-style: none;
}
.ui-autocomplete-results ul {
border: 1px solid #d3d3d3;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
color: #222222;
margin-bottom: -1px;
}
.ui-autocomplete-results li {
padding: .4em .5em;
color: #555555;
font-size: 1em;
font-weight: normal;
position: relative;
border-left: 0;
border-right: 0;
margin: 1px 0;
}
.ui-autocomplete-results li.ui-hover-state, .ui-autocomplete-results li.ui-active-state {
margin: 0;
}
/*Paul - you could add classes for these first last states instead for ie6... */
.ui-autocomplete-results li:first-child {
border-top: 0;
}
.ui-autocomplete-results li:last-child {
border-bottom: 0;
}
.ui-autocomplete-results li.ui-autocomplete-over {
border: 1px solid #999999;
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
color: #212121 !important;
}
.ui-autocomplete-results li.ui-autocomplete-active {
border: 1px solid #dddddd;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
color: #222222 !important;
outline: none;
}
/*UI ProgressBar */
.ui-progressbar {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
border: 1px solid #dddddd;
position: relative;
}
.ui-progressbar-bar {
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
overflow: visible;
position: relative;
border: 1px solid #d3d3d3;
margin-top: -1px;
margin-left: -1px;
margin-bottom: -1px;
}
.ui-progressbar-text {
color: #555555;
padding: .2em .5em;
font-weight: normal;
}
.ui-progressbar-text-back {
display: none;
/* position: absolute;
top: 1px;
left: 0px;
font-weight: normal;
color:#000;
padding-top: 1px;
padding-bottom: 1px;
padding-right: 1px;
*/
}
.ui-progressbar-disabled {
opacity:.5;
filter:Alpha(Opacity=50);
}
/*UI Colorpicker */
.ui-colorpicker {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
border: 4px solid #dddddd;
padding: 5px;
width: 360px;
position: relative;
}
.ui-colorpicker-color {
float: left;
width: 150px;
height: 150px;
margin-right: 15px;
}
.ui-colorpicker-color div { /* is this extra div needed? why not just .ui-colorpicker-color ? */
border: 1px solid #d3d3d3;
height: 150px;
background: url(images/_x_.);
position: relative;
}
.ui-colorpicker-color div div {/* shouldn't this have a class like ui-colorpicker-selector ? */
width: 11px;
height: 11px;
background: url(images/_x_.);
position: absolute;
border: 0;
margin: -5px 0 0 -5px;
float: none;
}
.ui-colorpicker-hue {
border: 1px solid #d3d3d3;
float: left;
width: 17px;
height: 150px;
background: url(images/_x_.);
position: relative;
margin-right: 15px;
}
.ui-colorpicker-hue div {
background:transparent url(images/_x_.); /*this image should be themerollable*/
height:9px;
left:-9px;
margin:-4px 0 0;
position:absolute;
width:35px;
cursor: ns-resize;
}
.ui-colorpicker-new-color, .ui-colorpicker-current-color {
float: left;
width: 6.5em;
height: 30px;
border: 1px solid #d3d3d3;
margin-right: 5px;
}
.ui-colorpicker-current-color {
margin-right: 0;
}
.ui-colorpicker-field, .ui-colorpicker-hex {
position: absolute;
width: 6em;
}
.ui-colorpicker-field label, .ui-colorpicker-field input,
.ui-colorpicker-hex label, .ui-colorpicker-hex input {
font-size: 1em;
color: #222222;
}
.ui-colorpicker-field label, .ui-colorpicker-hex label {
width: 1em;
margin-right: .3em;
}
.ui-colorpicker-field input, .ui-colorpicker-hex input {
border: 1px solid #d3d3d3;
width: 4.6em;
}
.ui-colorpicker-hex {
left: 205px;
top: 134px;
}
.ui-colorpicker-rgb-r {
top: 52px;
left: 205px;
}
.ui-colorpicker-rgb-g {
top: 78px;
left: 205px;
}
.ui-colorpicker-rgb-b {
top: 105px;
left: 205px;
}
.ui-colorpicker-hsb-h {
top: 52px;
left: 290px;
}
.ui-colorpicker-hsb-s {
top: 78px;
left: 290px;
}
.ui-colorpicker-hsb-b {
top: 105px;
left: 290px;
}
.ui-colorpicker-field label {
font-weight: normal;
}
.ui-colorpicker-field span {
width: 7px;
background: url(images/888888_11x11_icon_arrows_updown.gif) 50% 50% no-repeat;
right: 5px;
top: 0;
height: 20px;
position: absolute;
}
.ui-colorpicker-field span:hover {
background: url(images/454545_11x11_icon_arrows_updown.gif) 50% 50% no-repeat;
}
.ui-colorpicker-submit {
right: 14px;
top: 134px;
position: absolute;
}
/*
Generic ThemeRoller Classes
>> Make your jQuery Components ThemeRoller-Compatible!
*/
/*component global class*/
.ui-component {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Verdana, Arial, sans-serif;
font-size: 1.1em;
}
/*component content styles*/
.ui-component-content {
border: 1px solid #dddddd;
background: #ffffff url(images/ffffff_40x100_textures_01_flat_0.png) 0 0 repeat-x;
color: #222222;
}
.ui-component-content a {
color: #222222;
text-decoration: underline;
}
/*component states*/
.ui-default-state {
border: 1px solid #d3d3d3;
background: #e6e6e6 url(images/e6e6e6_40x100_textures_02_glass_75.png) 0 50% repeat-x;
font-weight: normal;
color: #555555 !important;
}
.ui-default-state a {
color: #555555;
}
.ui-default-state:hover, .ui-hover-state {
border: 1px solid #999999;
background: #dadada url(images/dadada_40x100_textures_02_glass_75.png) 0 50% repeat-x;
font-weight: normal;
color: #212121 !important;
}
.ui-hover-state a {
color: #212121;
}
.ui-default-state:active, .ui-active-state {
border: 1px solid #dddddd;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
font-weight: normal;
color: #222222 !important;
outline: none;
}
.ui-active-state a {
color: #222222;
outline: none;
}
/*icons*/
.ui-arrow-right-default {background: url(images/888888_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-right-default:hover, .ui-arrow-right-hover {background: url(images/454545_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-right-default:active, .ui-arrow-right-active {background: url(images/222222_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-right-content {background: url(images/222222_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-left-default {background: url(images/888888_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-left-default:hover, .ui-arrow-left-hover {background: url(images/454545_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-left-default:active, .ui-arrow-left-active {background: url(images/222222_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-left-content {background: url(images/222222_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-down-default {background: url(images/888888_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-down-default:hover, .ui-arrow-down-hover {background: url(images/454545_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-down-default:active, .ui-arrow-down-active {background: url(images/222222_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-down-content {background: url(images/222222_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-up-default {background: url(images/888888_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-arrow-up-default:hover, .ui-arrow-up-hover {background: url(images/454545_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-arrow-up-default:active, .ui-arrow-up-active {background: url(images/222222_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-arrow-up-content {background: url(images/222222_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-close-default {background: url(images/888888_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-close-default:hover, .ui-close-hover {background: url(images/454545_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-close-default:active, .ui-close-active {background: url(images/222222_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-close-content {background: url(images/222222_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-folder-closed-default {background: url(images/888888_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-closed-default:hover, .ui-folder-closed-hover {background: url(images/454545_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-closed-default:active, .ui-folder-closed-active {background: url(images/222222_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-closed-content {background: url(images/888888_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-open-default {background: url(images/888888_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-folder-open-default:hover, .ui-folder-open-hover {background: url(images/454545_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-folder-open-default:active, .ui-folder-open-active {background: url(images/222222_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-folder-open-content {background: url(images/222222_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-doc-default {background: url(images/888888_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-doc-default:hover, .ui-doc-hover {background: url(images/454545_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-doc-default:active, .ui-doc-active {background: url(images/222222_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-doc-content {background: url(images/222222_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-default {background: url(images/888888_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-default:hover, .ui-arrows-leftright-hover {background: url(images/454545_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-default:active, .ui-arrows-leftright-active {background: url(images/222222_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-content {background: url(images/222222_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-updown-default {background: url(images/888888_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-arrows-updown-default:hover, .ui-arrows-updown-hover {background: url(images/454545_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-arrows-updown-default:active, .ui-arrows-updown-active {background: url(images/222222_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-arrows-updown-content {background: url(images/222222_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-minus-default {background: url(images/888888_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-minus-default:hover, .ui-minus-hover {background: url(images/454545_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-minus-default:active, .ui-minus-active {background: url(images/222222_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-minus-content {background: url(images/222222_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-plus-default {background: url(images/888888_11x11_icon_plus.gif) no-repeat 50% 50%;}
.ui-plus-default:hover, .ui-plus-hover {background: url(images/454545_11x11_icon_plus.gif) no-repeat 50% 50%;}
.ui-plus-default:active, .ui-plus-active {background: url(images/222222_11x11_icon_plus.gif) no-repeat 50% 50%;}
.ui-plus-content {background: url(images/222222_11x11_icon_plus.gif) no-repeat 50% 50%;}
/*hidden elements*/
.ui-hidden {
display: none;/* for accessible hiding: position: absolute; left: -99999999px*/;
}
.ui-accessible-hidden {
position: absolute; left: -99999999px;
}
/*reset styles*/
.ui-reset {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
}
/*clearfix class*/
.ui-clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.ui-clearfix {display: inline-block;}
/* Hides from IE-mac \*/
* html .ui-clearfix {height: 1%;}
.ui-clearfix {display: block;}
/* End hide from IE-mac */
/* Note: for resizable styles, use the styles listed above in the dialog section */

12
htdocs/jquery.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

28
lib.py
View File

@ -251,3 +251,31 @@ def query_to_dict(results, descriptions):
output.append(row)
return output
def apply_query(queryset, params):
"""
Apply a dict-based set of filters & paramaters to a queryset.
queryset is a Django queryset, eg MyModel.objects.all() or
MyModel.objects.filter(user=request.user)
params is a dictionary that contains the following:
filtering: A dict of Django ORM filters, eg:
{'user__id__in': [1, 3, 103], 'title__contains': 'foo'}
other_filter: Another filter of some type, most likely a
set of Q() objects.
sorting: The name of the column to sort by
"""
for key in params['filtering'].keys():
filter = {key: params['filtering'][key]}
queryset = queryset.filter(**filter)
if params.get('other_filter', None):
# eg a Q() set
queryset = queryset.filter(params['other_filter'])
if params.get('sorting', None):
queryset = queryset.order_by(params['sorting'])
return queryset

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-08-19 07:01+0000\n"
"POT-Creation-Date: 2008-08-28 02:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,9 +18,9 @@ msgstr ""
#: forms.py:21 forms.py:147 templates/helpdesk/dashboard.html:10
#: templates/helpdesk/dashboard.html:26 templates/helpdesk/dashboard.html:41
#: templates/helpdesk/rss_list.html:23 templates/helpdesk/ticket_list.html:14
#: templates/helpdesk/ticket_list.html:25
#: templates/helpdesk/ticket_list.html:59
#: templates/helpdesk/rss_list.html:23 templates/helpdesk/ticket_list.html:30
#: templates/helpdesk/ticket_list.html:41
#: templates/helpdesk/ticket_list.html:115
msgid "Queue"
msgstr ""
@ -51,10 +51,10 @@ msgid ""
"this ticket immediately."
msgstr ""
#: forms.py:58 models.py:289 management/commands/escalate_tickets.py:152
#: forms.py:58 models.py:301 management/commands/escalate_tickets.py:152
#: templates/helpdesk/public_view_ticket.html:29
#: templates/helpdesk/ticket.html:67 templates/helpdesk/ticket.html.py:159
#: templates/helpdesk/ticket_list.html:27 views/staff.py:182
#: templates/helpdesk/ticket_list.html:43 views/staff.py:186
msgid "Priority"
msgstr ""
@ -105,14 +105,14 @@ msgstr ""
msgid "Ticket Opened Via Web"
msgstr ""
#: models.py:29 models.py:227 models.py:434 models.py:728 models.py:759
#: models.py:29 models.py:239 models.py:446 models.py:740 models.py:771
#: templates/helpdesk/dashboard.html:26 templates/helpdesk/dashboard.html:41
#: templates/helpdesk/ticket.html:153 templates/helpdesk/ticket_list.html:24
#: templates/helpdesk/ticket_list.html:59 views/staff.py:172
#: templates/helpdesk/ticket.html:153 templates/helpdesk/ticket_list.html:40
#: templates/helpdesk/ticket_list.html:115 views/staff.py:176
msgid "Title"
msgstr ""
#: models.py:34 models.py:733
#: models.py:34 models.py:745
msgid "Slug"
msgstr ""
@ -219,367 +219,401 @@ msgid ""
msgstr ""
#: models.py:118
msgid "E-Mail Username"
msgid "Use SSL for E-Mail?"
msgstr ""
#: models.py:122
msgid "Username for accessing this mailbox."
#: models.py:121
msgid ""
"Whether to use SSL for IMAP or POP3 - the default ports when using SSL are "
"993 for IMAP and 995 for POP3."
msgstr ""
#: models.py:126
msgid "E-Mail Password"
msgid "E-Mail Username"
msgstr ""
#: models.py:130
msgid "Password for the above username"
msgid "Username for accessing this mailbox."
msgstr ""
#: models.py:134
msgid "IMAP Folder"
msgid "E-Mail Password"
msgstr ""
#: models.py:138
msgid "Password for the above username"
msgstr ""
#: models.py:142
msgid "IMAP Folder"
msgstr ""
#: models.py:146
msgid ""
"If using IMAP, what folder do you wish to fetch messages from? This allows "
"you to use one IMAP account for multiple queues, by filtering messages on "
"your IMAP server into separate folders. Default: INBOX."
msgstr ""
#: models.py:145
#: models.py:153
msgid "E-Mail Check Interval"
msgstr ""
#: models.py:146
#: models.py:154
msgid "How often do you wish to check this mailbox? (in Minutes)"
msgstr ""
#: models.py:212 templates/helpdesk/dashboard.html:10
#: models.py:224 templates/helpdesk/dashboard.html:10
#: templates/helpdesk/ticket.html:123
msgid "Open"
msgstr ""
#: models.py:213 templates/helpdesk/ticket.html:128
#: models.py:225 templates/helpdesk/ticket.html:128
#: templates/helpdesk/ticket.html.py:133 templates/helpdesk/ticket.html:138
msgid "Reopened"
msgstr ""
#: models.py:214 templates/helpdesk/dashboard.html:10
#: models.py:226 templates/helpdesk/dashboard.html:10
#: templates/helpdesk/ticket.html:124 templates/helpdesk/ticket.html.py:129
#: templates/helpdesk/ticket.html:134
msgid "Resolved"
msgstr ""
#: models.py:215 templates/helpdesk/ticket.html:125
#: models.py:227 templates/helpdesk/ticket.html:125
#: templates/helpdesk/ticket.html.py:130 templates/helpdesk/ticket.html:135
#: templates/helpdesk/ticket.html.py:139
msgid "Closed"
msgstr ""
#: models.py:219
#: models.py:231
msgid "1. Critical"
msgstr ""
#: models.py:220
#: models.py:232
msgid "2. High"
msgstr ""
#: models.py:221
#: models.py:233
msgid "3. Normal"
msgstr ""
#: models.py:222
#: models.py:234
msgid "4. Low"
msgstr ""
#: models.py:223
#: models.py:235
msgid "5. Very Low"
msgstr ""
#: models.py:234 templates/helpdesk/dashboard.html:41
#: templates/helpdesk/ticket_list.html:23
#: templates/helpdesk/ticket_list.html:59
#: models.py:246 templates/helpdesk/dashboard.html:41
#: templates/helpdesk/ticket_list.html:39
#: templates/helpdesk/ticket_list.html:115
msgid "Created"
msgstr ""
#: models.py:236
#: models.py:248
msgid "Date this ticket was first created"
msgstr ""
#: models.py:240
#: models.py:252
msgid "Modified"
msgstr ""
#: models.py:242
#: models.py:254
msgid "Date this ticket was most recently changed."
msgstr ""
#: models.py:246 templates/helpdesk/public_view_ticket.html:24
#: models.py:258 templates/helpdesk/public_view_ticket.html:24
#: templates/helpdesk/ticket.html:62
msgid "Submitter E-Mail"
msgstr ""
#: models.py:249
#: models.py:261
msgid ""
"The submitter will receive an email for all public follow-ups left for this "
"task."
msgstr ""
#: models.py:261 templates/helpdesk/dashboard.html:26
#: templates/helpdesk/ticket_list.html:15
#: templates/helpdesk/ticket_list.html:26
#: templates/helpdesk/ticket_list.html:59
#: models.py:273 templates/helpdesk/dashboard.html:26
#: templates/helpdesk/ticket_list.html:31
#: templates/helpdesk/ticket_list.html:42
#: templates/helpdesk/ticket_list.html:115
msgid "Status"
msgstr ""
#: models.py:267
#: models.py:279
msgid "On Hold"
msgstr ""
#: models.py:270
#: models.py:282
msgid "If a ticket is on hold, it will not automatically be escalated."
msgstr ""
#: models.py:275 models.py:737 templates/helpdesk/public_view_ticket.html:34
#: models.py:287 models.py:749 templates/helpdesk/public_view_ticket.html:34
#: templates/helpdesk/ticket.html:72
msgid "Description"
msgstr ""
#: models.py:278
#: models.py:290
msgid "The content of the customers query."
msgstr ""
#: models.py:282 templates/helpdesk/public_view_ticket.html:41
#: models.py:294 templates/helpdesk/public_view_ticket.html:41
#: templates/helpdesk/ticket.html:79
msgid "Resolution"
msgstr ""
#: models.py:285
#: models.py:297
msgid "The resolution provided to the customer by our staff."
msgstr ""
#: models.py:293
#: models.py:305
msgid "1 = Highest Priority, 5 = Low Priority"
msgstr ""
#: models.py:300
#: models.py:312
msgid ""
"The date this ticket was last escalated - updated automatically by "
"management/commands/escalate_tickets.py."
msgstr ""
#: models.py:309 templates/helpdesk/ticket.html:58 views/feeds.py:91
#: views/feeds.py:117 views/feeds.py:171 views/staff.py:149
#: models.py:321 templates/helpdesk/ticket.html:58 views/feeds.py:91
#: views/feeds.py:117 views/feeds.py:171 views/staff.py:153
msgid "Unassigned"
msgstr ""
#: models.py:348
#: models.py:360
msgid " - On Hold"
msgstr ""
#: models.py:430 models.py:662
#: models.py:442 models.py:674
msgid "Date"
msgstr ""
#: models.py:441 views/staff.py:163
#: models.py:453 views/staff.py:167
msgid "Comment"
msgstr ""
#: models.py:447
#: models.py:459
msgid "Public"
msgstr ""
#: models.py:450
#: models.py:462
msgid ""
"Public tickets are viewable by the submitter and all staff, but non-public "
"tickets can only be seen by staff."
msgstr ""
#: models.py:461 templates/helpdesk/ticket.html:121
#: models.py:473 templates/helpdesk/ticket.html:121
msgid "New Status"
msgstr ""
#: models.py:465
#: models.py:477
msgid "If the status was changed, what was it changed to?"
msgstr ""
#: models.py:496
#: models.py:508
msgid "Field"
msgstr ""
#: models.py:501
#: models.py:513
msgid "Old Value"
msgstr ""
#: models.py:507
#: models.py:519
msgid "New Value"
msgstr ""
#: models.py:515
#: models.py:527
msgid "removed"
msgstr ""
#: models.py:517
#: models.py:529
#, python-format
msgid "set to %s"
msgstr ""
#: models.py:519
#: models.py:531
#, python-format
msgid "changed from \"%(old_value)s\" to \"%(new_value)s\""
msgstr ""
#: models.py:562
#: models.py:574
msgid "File"
msgstr ""
#: models.py:567
#: models.py:579
msgid "Filename"
msgstr ""
#: models.py:572
#: models.py:584
msgid "MIME Type"
msgstr ""
#: models.py:577
#: models.py:589
msgid "Size"
msgstr ""
#: models.py:578
#: models.py:590
msgid "Size of this file in bytes"
msgstr ""
#: models.py:611
#: models.py:623
msgid ""
"Leave blank to allow this reply to be used for all queues, or select those "
"queues you wish to limit this reply to."
msgstr ""
#: models.py:616 models.py:657
#: models.py:628 models.py:669
msgid "Name"
msgstr ""
#: models.py:618
#: models.py:630
msgid ""
"Only used to assist users with selecting a reply - not shown to the user."
msgstr ""
#: models.py:623
#: models.py:635
msgid "Body"
msgstr ""
#: models.py:624
#: models.py:636
msgid ""
"Context available: {{ ticket }} - ticket object (eg {{ ticket.title }}); "
"{{ queue }} - The queue; and {{ user }} - the current user."
msgstr ""
#: models.py:651
#: models.py:663
msgid ""
"Leave blank for this exclusion to be applied to all queues, or select those "
"queues you wish to exclude with this entry."
msgstr ""
#: models.py:663
#: models.py:675
msgid "Date on which escalation should not happen"
msgstr ""
#: models.py:680
#: models.py:692
msgid "Template Name"
msgstr ""
#: models.py:686
#: models.py:698
msgid "Subject"
msgstr ""
#: models.py:688
#: models.py:700
msgid ""
"This will be prefixed with \"[ticket.ticket] ticket.title\". We recommend "
"something simple such as \"(Updated\") or \"(Closed)\" - the same context is "
"available as in plain_text, below."
msgstr ""
#: models.py:694
#: models.py:706
msgid "Heading"
msgstr ""
#: models.py:696
#: models.py:708
msgid ""
"In HTML e-mails, this will be the heading at the top of the email - the same "
"context is available as in plain_text, below."
msgstr ""
#: models.py:702
#: models.py:714
msgid "Plain Text"
msgstr ""
#: models.py:703
#: models.py:715
msgid ""
"The context available to you includes {{ ticket }}, {{ queue }}, and "
"depending on the time of the call: {{ resolution }} or {{ comment }}."
msgstr ""
#: models.py:709
#: models.py:721
msgid "HTML"
msgstr ""
#: models.py:710
#: models.py:722
msgid "The same context is available here as in plain_text, above."
msgstr ""
#: models.py:764
#: models.py:776
msgid "Question"
msgstr ""
#: models.py:768
#: models.py:780
msgid "Answer"
msgstr ""
#: models.py:772
#: models.py:784
msgid "Votes"
msgstr ""
#: models.py:773
#: models.py:785
msgid "Total number of votes cast for this item"
msgstr ""
#: models.py:777
#: models.py:789
msgid "Positive Votes"
msgstr ""
#: models.py:778
#: models.py:790
msgid "Number of votes for this item which were POSITIVE."
msgstr ""
#: models.py:782
#: models.py:794
msgid "Last Updated"
msgstr ""
#: models.py:783
#: models.py:795
msgid "The date on which this question was most recently changed."
msgstr ""
#: models.py:795
#: models.py:807
msgid "Unrated"
msgstr ""
#: models.py:835 templates/helpdesk/ticket_list.html:84
msgid "Query Name"
msgstr ""
#: models.py:837
msgid "User-provided name for this query"
msgstr ""
#: models.py:841
msgid "Shared With Other Users?"
msgstr ""
#: models.py:845
msgid "Should other users see this query?"
msgstr ""
#: models.py:849
msgid "Search Query"
msgstr ""
#: models.py:850
msgid "Pickled query object. Be wary changing this."
msgstr ""
#: management/commands/escalate_tickets.py:146
#, python-format
msgid "Ticket escalated after %s days"
msgstr ""
#: management/commands/get_email.py:97
#: management/commands/get_email.py:108
msgid "Created from e-mail"
msgstr ""
#: management/commands/get_email.py:100
#: management/commands/get_email.py:111
msgid "Unknown Sender"
msgstr ""
#: management/commands/get_email.py:206
#: management/commands/get_email.py:215
msgid " (Updated)"
msgstr ""
#: management/commands/get_email.py:228
#: management/commands/get_email.py:237
#, python-format
msgid "E-Mail Received from %(sender_email)s"
msgstr ""
@ -588,68 +622,99 @@ msgstr ""
msgid "Powered by Jutda Helpdesk"
msgstr ""
#: templates/helpdesk/base.html:7 templates/helpdesk/rss_list.html:9
#: templates/helpdesk/base.html:9 templates/helpdesk/rss_list.html:9
#: templates/helpdesk/rss_list.html:23 templates/helpdesk/rss_list.html:28
msgid "My Open Tickets"
msgstr ""
#: templates/helpdesk/base.html:8
#: templates/helpdesk/base.html:10
msgid "All Recent Activity"
msgstr ""
#: templates/helpdesk/base.html:9 templates/helpdesk/dashboard.html:40
#: templates/helpdesk/base.html:11 templates/helpdesk/dashboard.html:40
#: templates/helpdesk/rss_list.html:15
msgid "Unassigned Tickets"
msgstr ""
#: templates/helpdesk/base.html:15 templates/helpdesk/public_base.html:3
#: templates/helpdesk/base.html:17 templates/helpdesk/public_base.html:3
#: templates/helpdesk/public_base.html:11
msgid "Helpdesk"
msgstr ""
#: templates/helpdesk/base.html:17
#: templates/helpdesk/base.html:19
msgid "Dashboard"
msgstr ""
#: templates/helpdesk/base.html:18 templates/helpdesk/ticket_list.html:58
#: templates/helpdesk/base.html:20 templates/helpdesk/ticket_list.html:114
msgid "Tickets"
msgstr ""
#: templates/helpdesk/base.html:19
#: templates/helpdesk/base.html:21
msgid "New Ticket"
msgstr ""
#: templates/helpdesk/base.html:20
#: templates/helpdesk/base.html:22
msgid "Stats"
msgstr ""
#: templates/helpdesk/base.html:21
#: templates/helpdesk/base.html:23
msgid "Logout"
msgstr ""
#: templates/helpdesk/base.html:22
#: templates/helpdesk/base.html:24
msgid "Search..."
msgstr ""
#: templates/helpdesk/base.html:29
#: templates/helpdesk/base.html:31
msgid "Powered by <a href='http://www.jutda.com.au/'>Jutda Helpdesk</a>."
msgstr ""
#: templates/helpdesk/base.html:29 templates/helpdesk/rss_list.html:9
#: templates/helpdesk/base.html:31 templates/helpdesk/rss_list.html:9
#: templates/helpdesk/rss_list.html:12 templates/helpdesk/rss_list.html:15
#: templates/helpdesk/rss_list.html:27 templates/helpdesk/rss_list.html:28
msgid "RSS Icon"
msgstr ""
#: templates/helpdesk/base.html:29 templates/helpdesk/rss_list.html:2
#: templates/helpdesk/base.html:31 templates/helpdesk/rss_list.html:2
#: templates/helpdesk/rss_list.html.py:4
msgid "RSS Feeds"
msgstr ""
#: templates/helpdesk/base.html:29
#: templates/helpdesk/base.html:31
msgid "API"
msgstr ""
#: templates/helpdesk/confirm_delete_saved_query.html:3
msgid "Delete Saved Query"
msgstr ""
#: templates/helpdesk/confirm_delete_saved_query.html:5
#, python-format
msgid ""
"\n"
"<h2>Delete Query</h2>\n"
"\n"
"<p>Are you sure you want to delete this saved filter (<em>%(query_title)s</"
"em>)? To re-create it, you will need to manually re-filter your ticket "
"listing.</p>\n"
msgstr ""
#: templates/helpdesk/confirm_delete_saved_query.html:11
msgid ""
"\n"
"<p>You have shared this query, so other users may be using it. If you delete "
"it, they will have to manually create their own query.</p>\n"
msgstr ""
#: templates/helpdesk/confirm_delete_saved_query.html:15
#: templates/helpdesk/delete_ticket.html:11
msgid ""
"<p><a href='../'>No, Don't Delete It</a></p>\n"
"\n"
"<form method='post' action='./'><input type='submit' value='Yes - Delete "
"It' /></form>\n"
msgstr ""
#: templates/helpdesk/create_ticket.html:3
msgid "Create Ticket"
msgstr ""
@ -685,7 +750,7 @@ msgid "Your Tickets"
msgstr ""
#: templates/helpdesk/dashboard.html:26 templates/helpdesk/dashboard.html:41
#: templates/helpdesk/ticket_list.html:59
#: templates/helpdesk/ticket_list.html:115
msgid "Pr"
msgstr ""
@ -710,11 +775,6 @@ msgid ""
"<p>Are you sure you want to delete this ticket (<em>%(ticket_title)s</em>)? "
"All traces of the ticket, including followups, attachments, and updates will "
"be irreversably removed.</p>\n"
"\n"
"<p><a href='../'>No, Don't Delete It</a></p>\n"
"\n"
"<form method='post' action='./'><input type='submit' value='Yes - Delete "
"It' /></form>\n"
msgstr ""
#: templates/helpdesk/kb_category.html:4
@ -1036,9 +1096,9 @@ msgstr ""
msgid "Change Further Details &raquo;"
msgstr ""
#: templates/helpdesk/ticket.html:156 templates/helpdesk/ticket_list.html:13
#: templates/helpdesk/ticket_list.html:28
#: templates/helpdesk/ticket_list.html:59
#: templates/helpdesk/ticket.html:156 templates/helpdesk/ticket_list.html:29
#: templates/helpdesk/ticket_list.html:44
#: templates/helpdesk/ticket_list.html:115
msgid "Owner"
msgstr ""
@ -1062,32 +1122,74 @@ msgstr ""
msgid "Ticket Listing"
msgstr ""
#: templates/helpdesk/ticket_list.html:12
#: templates/helpdesk/ticket_list.html:26
msgid "Change Query"
msgstr ""
#: templates/helpdesk/ticket_list.html:28
msgid "Sorting"
msgstr ""
#: templates/helpdesk/ticket_list.html:16
#: templates/helpdesk/ticket_list.html:50
#: templates/helpdesk/ticket_list.html:32
#: templates/helpdesk/ticket_list.html:70
msgid "Keywords"
msgstr ""
#: templates/helpdesk/ticket_list.html:34
#: templates/helpdesk/ticket_list.html:51
msgid "Owner(s)"
msgstr ""
#: templates/helpdesk/ticket_list.html:39
#: templates/helpdesk/ticket_list.html:57
msgid "Queue(s)"
msgstr ""
#: templates/helpdesk/ticket_list.html:44
#: templates/helpdesk/ticket_list.html:63
msgid "Status(es)"
msgstr ""
#: templates/helpdesk/ticket_list.html:54
#: templates/helpdesk/ticket_list.html:75
msgid "Apply Filter"
msgstr ""
#: templates/helpdesk/ticket_list.html:71
#: templates/helpdesk/ticket_list.html:80
#: templates/helpdesk/ticket_list.html:95
msgid "Save Query"
msgstr ""
#: templates/helpdesk/ticket_list.html:86
msgid ""
"This name appears in the drop-down list of saved queries. If you share your "
"query, other users will see this name, so choose something clear and "
"descriptive!"
msgstr ""
#: templates/helpdesk/ticket_list.html:88
msgid "Shared?"
msgstr ""
#: templates/helpdesk/ticket_list.html:89
msgid "Yes, share this query with other users."
msgstr ""
#: templates/helpdesk/ticket_list.html:90
msgid ""
"If you share this query, it will be visible by <em>all</em> other logged-in "
"users."
msgstr ""
#: templates/helpdesk/ticket_list.html:102
msgid "Use Saved Query"
msgstr ""
#: templates/helpdesk/ticket_list.html:104
msgid "Query"
msgstr ""
#: templates/helpdesk/ticket_list.html:109
msgid "Run Query"
msgstr ""
#: templates/helpdesk/ticket_list.html:127
msgid "No Tickets Match Your Selection"
msgstr ""
@ -1183,23 +1285,23 @@ msgstr ""
msgid "Invalid ticket ID or e-mail address. Please try again."
msgstr ""
#: views/staff.py:107
#: views/staff.py:111
msgid "Accepted resolution and closed ticket"
msgstr ""
#: views/staff.py:143
#: views/staff.py:147
#, python-format
msgid "Assigned to %(username)s"
msgstr ""
#: views/staff.py:165
#: views/staff.py:169
msgid "Updated"
msgstr ""
#: views/staff.py:369
#: views/staff.py:401
msgid "Ticket taken off hold"
msgstr ""
#: views/staff.py:372
#: views/staff.py:404
msgid "Ticket placed on hold"
msgstr ""

View File

@ -817,3 +817,41 @@ class KBItem(models.Model):
return ('helpdesk_kb_item', [str(self.id)])
get_absolute_url = models.permalink(get_absolute_url)
class SavedSearch(models.Model):
"""
Allow a user to save a ticket search, eg their filtering and sorting
options, and optionally share it with other users. This lets people
easily create a set of commonly-used filters, such as:
* My tickets waiting on me
* My tickets waiting on submitter
* My tickets in 'Priority Support' queue with priority of 1
* All tickets containing the word 'billing'.
etc...
"""
user = models.ForeignKey(User)
title = models.CharField(
_('Query Name'),
max_length=100,
help_text=_('User-provided name for this query'),
)
shared = models.BooleanField(
_('Shared With Other Users?'),
default=False,
blank=True,
null=True,
help_text=_('Should other users see this query?'),
)
query = models.TextField(
_('Search Query'),
help_text=_('Pickled query object. Be wary changing this.'),
)
def __unicode__(self):
if self.shared:
return u'%s (*)' % self.title
else:
return u'%s' % self.title

View File

@ -3,7 +3,9 @@
<head>
<title>{% block helpdesk_title %}Helpdesk{% endblock %} :: {% trans "Powered by Jutda Helpdesk" %}</title>
<script src='{{ MEDIA_URL }}/helpdesk/jquery.js' type='text/javascript' language='javascript'></script>
<script src='{{ MEDIA_URL }}/helpdesk/jquery.ui-1.6b.all.packed.js' type='text/javascript' language='javascript'></script>
<link rel='stylesheet' href='{{ MEDIA_URL }}/helpdesk/helpdesk.css' type='text/css' />
<link rel='stylesheet' href='{{ MEDIA_URL }}/helpdesk/jquery-smoothness-theme/jquery-ui-themeroller.css' type='text/css' />
<link rel='alternate' href='{% url helpdesk_rss "user" %}{{ user.username }}/' type='application/rss+xml' title='{% trans "My Open Tickets" %}' />
<link rel='alternate' href='{% url helpdesk_rss "recent" %}' type='application/rss+xml' title='{% trans "All Recent Activity" %}' />
<link rel='alternate' href='{% url helpdesk_rss "unassigned" %}' type='application/rss+xml' title='{% trans "Unassigned Tickets" %}' />

View File

@ -0,0 +1,18 @@
{% extends "helpdesk/base.html" %}{% load i18n %}
{% block helpdesk_title %}{% trans "Delete Saved Query" %}{% endblock %}
{% block helpdesk_body %}{% blocktrans with query.title as query_title %}
<h2>Delete Query</h2>
<p>Are you sure you want to delete this saved filter (<em>{{ query_title }}</em>)? To re-create it, you will need to manually re-filter your ticket listing.</p>
{% endblocktrans %}
{% if query.shared %}{% blocktrans %}
<p>You have shared this query, so other users may be using it. If you delete it, they will have to manually create their own query.</p>
{% endblocktrans %}{% endif %}
{% blocktrans %}<p><a href='../'>No, Don't Delete It</a></p>
<form method='post' action='./'><input type='submit' value='Yes - Delete It' /></form>
{% endblocktrans %}{% endblock %}

View File

@ -6,8 +6,9 @@
<h2>Delete Ticket</h2>
<p>Are you sure you want to delete this ticket (<em>{{ ticket_title }}</em>)? All traces of the ticket, including followups, attachments, and updates will be irreversably removed.</p>
{% endblocktrans %}
<p><a href='../'>No, Don't Delete It</a></p>
{% blocktrans %}<p><a href='../'>No, Don't Delete It</a></p>
<form method='post' action='./'><input type='submit' value='Yes - Delete It' /></form>
{% endblocktrans %}{% endblock %}

View File

@ -3,11 +3,27 @@
{% block helpdesk_head %}
<script type='text/javascript' language='javascript' src='{{ MEDIA_URL }}/helpdesk/filter.js'></script>
<script type='text/javascript' language='javascript' src='{{ MEDIA_URL }}/helpdesk/hover.js'></script>
<script type='text/javascript' language='javascript'>
$(document).ready(function() {
$("#tabset > ul").tabs();
});
</script>
{% endblock %}
{% block helpdesk_body %}
{% load in_list %}
<div id='tabset'>
<ul>
<li class='ui-tabs-nav-item'><a href='#tabfilter'><span>Query Options</span></a></li>
{% if not from_saved_query %}<li class='ui-tabs-nav-item'><a href='#tabsave'><span>Save This Query</span></a></li>{% endif %}
<li class='ui-tabs-nav-item'><a href='#tabload'><span>Load Saved Query</span></a></li>
</ul>
<div id='tabfilter'>
<h3>{% trans "Change Query" %}</h3>
<form><select name='select' id='filterBuilderSelect'>
<option value='Sort'>{% trans "Sorting" %}</option>
<option value='Owner'>{% trans "Owner" %}</option>
@ -18,41 +34,81 @@
<input type='button' id='filterBuilderButton' value='+' /></form>
<form method='get' action='./'>
<div class='filterBox{% if sort %} filterBoxShow{% endif %}' id='filterBoxSort'>
<label for='id_sort'>Sorting</label> <select id='id_sort' name='sort'>
<option value='created'{% ifequal sort "created"%} selected='selected'{% endifequal %}>{% trans "Created" %}</option>
<option value='title'{% ifequal sort "title"%} selected='selected'{% endifequal %}>{% trans "Title" %}</option>
<option value='queue'{% ifequal sort "queue"%} selected='selected'{% endifequal %}>{% trans "Queue" %}</option>
<option value='status'{% ifequal sort "status"%} selected='selected'{% endifequal %}>{% trans "Status" %}</option>
<option value='priority'{% ifequal sort "priority"%} selected='selected'{% endifequal %}>{% trans "Priority" %}</option>
<option value='assigned_to'{% ifequal sort "assigned_to"%} selected='selected'{% endifequal %}>{% trans "Owner" %}</option>
<div class='filterBox{% if query_params.sorting %} filterBoxShow{% endif %}' id='filterBoxSort'>
<label for='id_sort'>Sorting</label><select id='id_sort' name='sort'>
<option value='created'{% ifequal query_params.sorting "created"%} selected='selected'{% endifequal %}>{% trans "Created" %}</option>
<option value='title'{% ifequal query_params.sorting "title"%} selected='selected'{% endifequal %}>{% trans "Title" %}</option>
<option value='queue'{% ifequal query_params.sorting "queue"%} selected='selected'{% endifequal %}>{% trans "Queue" %}</option>
<option value='status'{% ifequal query_params.sorting "status"%} selected='selected'{% endifequal %}>{% trans "Status" %}</option>
<option value='priority'{% ifequal query_params.sorting "priority"%} selected='selected'{% endifequal %}>{% trans "Priority" %}</option>
<option value='assigned_to'{% ifequal query_params.sorting "assigned_to"%} selected='selected'{% endifequal %}>{% trans "Owner" %}</option>
</select>
<p class='filterHelp'>Ordering applied to tickets</p>
<input type='button' class='filterBuilderRemove' value='-' />
</div>
<div class='filterBox{% if owners %} filterBoxShow{% endif %}' id='filterBoxOwner'>
<label for='id_owners'>{% trans "Owner(s)" %}</label> <select id='id_owners' name='assigned_to' multiple='selected' size='5'>{% for u in user_choices %}<option value='{{ u.id }}'{% if u.id|in_list:owners %} selected='selected'{% endif %}>{{ u.username }}</option>{% endfor %}</select>
<div class='filterBox{% if query_params.filtering.assigned_to__id__in %} filterBoxShow{% endif %}' id='filterBoxOwner'>
<label for='id_owners'>{% trans "Owner(s)" %}</label><select id='id_owners' name='assigned_to' multiple='selected' size='5'>{% for u in user_choices %}<option value='{{ u.id }}'{% if u.id|in_list:query_params.filtering.assigned_to__id__in %} selected='selected'{% endif %}>{{ u.username }}{% ifequal u user %} (ME){% endifequal %}</option>{% endfor %}</select>
<p class='filterHelp'>Ctrl-Click to select multiple options</p>
<input type='button' class='filterBuilderRemove' value='-' />
</div>
<div class='filterBox{% if queues %} filterBoxShow{% endif %}' id='filterBoxQueue'>
<label for='id_queues'>{% trans "Queue(s)" %}</label> <select id='id_queues' name='queue' multiple='selected' size='5'>{% for q in queue_choices %}<option value='{{ q.id }}'{% if q.id|in_list:queues %} selected='selected'{% endif %}>{{ q.title }}</option>{% endfor %}</select>
<div class='filterBox{% if query_params.filtering.queue__id__in %} filterBoxShow{% endif %}' id='filterBoxQueue'>
<label for='id_queues'>{% trans "Queue(s)" %}</label><select id='id_queues' name='queue' multiple='selected' size='5'>{% for q in queue_choices %}<option value='{{ q.id }}'{% if q.id|in_list:query_params.filtering.queue__id__in %} selected='selected'{% endif %}>{{ q.title }}</option>{% endfor %}</select>
<p class='filterHelp'>Ctrl-click to select multiple options</p>
<input type='button' class='filterBuilderRemove' value='-' />
</div>
<div class='filterBox{% if statuses %} filterBoxShow{% endif %}' id='filterBoxStatus'>
<label for='id_statuses'>{% trans "Status(es)" %}</label> {% for s in status_choices %}<input type='checkbox' name='status' value='{{ s.0 }}'{% if s.0|in_list:statuses %} checked='checked'{% endif %}> {{ s.1 }}{% endfor %}
<div class='filterBox{% if query_params.filtering.status__in %} filterBoxShow{% endif %}' id='filterBoxStatus'>
<label for='id_statuses'>{% trans "Status(es)" %}</label><select id='id_statuses' name='status' multiple='selected' size='5'>{% for s in status_choices %}<option value='{{ s.0 }}'{% if s.0|in_list:query_params.filtering.status__in %} selected='selected'{% endif %}>{{ s.1 }}</option>{% endfor %}</select>
<p class='filterHelp'>Ctrl-click to select multiple options</p>
<input type='button' class='filterBuilderRemove' value='-' />
</div>
<div class='filterBox{% if query %} filterBoxShow{% endif %}' id='filterBoxKeywords'>
<label for='id_query'>{% trans "Keywords" %}</label> <input type='text' name='q' value='{{ query }}' id='id_query' />
<label for='id_query'>{% trans "Keywords" %}</label><input type='text' name='q' value='{{ query }}' id='id_query' />
<p class='filterHelp'>Keywords are case-insensitive, and will be looked for in the title, body and submitter fields.</p>
<input type='button' class='filterBuilderRemove' value='-' />
</div>
<hr style='clear: both;' />
<input type='submit' value='{% trans "Apply Filter" %}' />
</form>
</div>
{% if not from_saved_query %}<div class='tab' id='tabsave'>
<h3>{% trans "Save Query" %}</h3>
<form method='post' action='{% url helpdesk_savequery %}'>
<input type='hidden' name='query_encoded' value='{{ urlsafe_query }}' />
<dl>
<dt><label for='id_title'>{% trans "Query Name" %}</label></dt>
<dd><input type='text' name='title' id='id_title' /></dd>
<dd class='form_help_text'>{% trans "This name appears in the drop-down list of saved queries. If you share your query, other users will see this name, so choose something clear and descriptive!" %}</dd>
<dt><label for='id_shared'>{% trans "Shared?" %}</label></dt>
<dd><input type='checkbox' name='shared' id='id_shared' /> {% trans "Yes, share this query with other users." %}</dd>
<dd class='form_help_text'>{% trans "If you share this query, it will be visible by <em>all</em> other logged-in users." %}</dd>
</dl>
<div class='buttons'>
<input type='submit' value='{% trans "Save Query" %}'>
</div>
</form>
</div>{% endif %}
<div id='tabload'>
<h3>{% trans "Use Saved Query" %}</h3>
<form method='get' action='{% url helpdesk_list %}'>
<p><label for='id_query_selector'>{% trans "Query" %}</label> <select name='saved_query' id='id_query_selector' />
{% for q in user_saved_queries %}
<option value='{{ q.id }}'>{{ q.title }}{% if q.shared %} (Shared{% ifnotequal user q.user %} by {{ q.user.username }}{% endifnotequal %}){% endif %}</option>
{% endfor %}
</select></p>
<input type='submit' value='{% trans "Run Query" %}'>
</form>
</div>
<table width='100%'>
<tr class='row_tablehead'><td colspan='7'>{% trans "Tickets" %}</td></tr>

View File

@ -61,6 +61,14 @@ urlpatterns = patterns('helpdesk.views.staff',
url(r'^reports/(?P<report>\w+)/$',
'run_report',
name='helpdesk_run_report'),
url(r'^save_query/$',
'save_query',
name='helpdesk_savequery'),
url(r'^delete_query/(?P<id>[0-9]+)/$',
'delete_saved_query',
name='helpdesk_delete_query'),
)
urlpatterns += patterns('helpdesk.views.public',

View File

@ -21,8 +21,8 @@ from django.template import loader, Context, RequestContext
from django.utils.translation import ugettext as _
from helpdesk.forms import TicketForm
from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict
from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment
from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict, apply_query
from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch
def dashboard(request):
@ -273,47 +273,71 @@ update_ticket = login_required(update_ticket)
def ticket_list(request):
tickets = Ticket.objects.select_related()
context = {}
# Query_params will hold a dictionary of paramaters relating to
# a query, to be saved if needed:
query_params = {
'filtering': {},
'sorting': None,
'keyword': None,
'other_filter': None,
}
### FILTERING
queues = request.GET.getlist('queue')
if queues:
queues = [int(q) for q in queues]
tickets = tickets.filter(queue__id__in=queues)
context = dict(context, queues=queues)
if request.GET.get('saved_query', None):
from_saved_query = True
try:
saved_query = SavedSearch.objects.get(pk=request.GET.get('saved_query'))
except SavedSearch.DoesNotExist:
return HttpResponseRedirect(reverse('helpdesk_list'))
if not (saved_query.shared or saved_query.user == request.user):
return HttpResponseRedirect(reverse('helpdesk_list'))
owners = request.GET.getlist('assigned_to')
if owners:
owners = [int(u) for u in owners]
tickets = tickets.filter(assigned_to__id__in=owners)
context = dict(context, owners=owners)
import base64, cPickle
query_params = cPickle.loads(base64.urlsafe_b64decode(str(saved_query.query)))
else:
from_saved_query = False
queues = request.GET.getlist('queue')
if queues:
queues = [int(q) for q in queues]
query_params['filtering']['queue__id__in'] = queues
statuses = request.GET.getlist('status')
if statuses:
statuses = [int(s) for s in statuses]
tickets = tickets.filter(status__in=statuses)
context = dict(context, statuses=statuses)
owners = request.GET.getlist('assigned_to')
if owners:
owners = [int(u) for u in owners]
query_params['filtering']['assigned_to__id__in'] = owners
### KEYWORD SEARCHING
q = request.GET.get('q', None)
statuses = request.GET.getlist('status')
if statuses:
statuses = [int(s) for s in statuses]
query_params['filtering']['status__in'] = statuses
if q:
qset = (
Q(title__icontains=q) |
Q(description__icontains=q) |
Q(resolution__icontains=q) |
Q(submitter_email__icontains=q)
)
tickets = tickets.filter(qset)
context = dict(context, query=q)
### KEYWORD SEARCHING
q = request.GET.get('q', None)
### SORTING
sort = request.GET.get('sort', None)
if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'):
sort = 'created'
tickets = tickets.order_by(sort)
context = dict(context, sort=sort)
if q:
qset = (
Q(title__icontains=q) |
Q(description__icontains=q) |
Q(resolution__icontains=q) |
Q(submitter_email__icontains=q)
)
context = dict(context, query=q)
query_params['other_filter'] = qset
### SORTING
sort = request.GET.get('sort', None)
if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'):
sort = 'created'
query_params['sorting'] = sort
tickets = apply_query(Ticket.objects.select_related(), query_params)
import cPickle, base64
urlsafe_query = base64.urlsafe_b64encode(cPickle.dumps(query_params))
user_saved_queries = SavedSearch.objects.filter(Q(user=request.user) | Q(shared__exact=True))
return render_to_response('helpdesk/ticket_list.html',
RequestContext(request, dict(
@ -322,6 +346,10 @@ def ticket_list(request):
user_choices=User.objects.filter(is_active=True),
queue_choices=Queue.objects.all(),
status_choices=Ticket.STATUS_CHOICES,
urlsafe_query=urlsafe_query,
user_saved_queries=user_saved_queries,
query_params=query_params,
from_saved_query=from_saved_query,
)))
ticket_list = login_required(ticket_list)
@ -559,3 +587,31 @@ def run_report(request, report):
}))
run_report = login_required(run_report)
def save_query(request):
title = request.POST.get('title', None)
shared = request.POST.get('shared', False)
query_encoded = request.POST.get('query_encoded', None)
if not title or not query_encoded:
return HttpResponseRedirect(reverse('helpdesk_list'))
query = SavedSearch(title=title, shared=shared, query=query_encoded, user=request.user)
query.save()
return HttpResponseRedirect('%s?saved_query=%s' % (reverse('helpdesk_list'), query.id))
save_query = login_required(save_query)
def delete_saved_query(request, id):
query = get_object_or_404(SavedSearch, id=id, user=request.user)
if request.method == 'POST':
query.delete()
return HttpResponseRedirect(reverse('helpdesk_list'))
else:
return render_to_response('helpdesk/confirm_delete_saved_query.html',
RequestContext(request, {
'query': query,
}))
delete_saved_query = login_required(delete_saved_query)