gruffen commited on 2011-08-25 09:33:26
Showing 15 changed files, with 1589 additions and 73 deletions.
... | ... |
@@ -37,6 +37,8 @@ if (file_exists(dirname(__FILE__) . '/SSI.php') && !defined('SMF')) |
37 | 37 |
require_once(dirname(__FILE__) . '/SSI.php'); |
38 | 38 |
elseif (!defined('SMF')) // If we are outside SMF and can't find SSI.php, then throw an error |
39 | 39 |
die('<b>Error:</b> Cannot install - please verify you put this file in the same place as SMF\'s SSI.php.'); |
40 |
+elseif (@version_compare(PHP_VERSION, '4.3.0', '<')) |
|
41 |
+ die('<b>Error:</b> SimpleDesk 2.0 requires PHP 4.3.0 to be installed on your server.'); |
|
40 | 42 |
|
41 | 43 |
if (SMF == 'SSI') |
42 | 44 |
db_extend('packages'); |
... | ... |
@@ -536,6 +538,38 @@ $tables[] = array( |
536 | 538 |
'error' => 'fatal', |
537 | 539 |
'parameters' => array(), |
538 | 540 |
); |
541 |
+$tables[] = array( |
|
542 |
+ 'table_name' => '{db_prefix}helpdesk_search_ticket_words', |
|
543 |
+ 'columns' => array( |
|
544 |
+ db_field('id_word', 'bigint'), |
|
545 |
+ db_field('id_msg', 'int'), |
|
546 |
+ ), |
|
547 |
+ 'indexes' => array( |
|
548 |
+ array( |
|
549 |
+ 'columns' => array('id_word', 'id_msg'), |
|
550 |
+ 'type' => 'primary', |
|
551 |
+ ), |
|
552 |
+ ), |
|
553 |
+ 'if_exists' => 'ignore', |
|
554 |
+ 'error' => 'fatal', |
|
555 |
+ 'parameters' => array(), |
|
556 |
+); |
|
557 |
+$tables[] = array( |
|
558 |
+ 'table_name' => '{db_prefix}helpdesk_search_subject_words', |
|
559 |
+ 'columns' => array( |
|
560 |
+ db_field('id_word', 'bigint'), |
|
561 |
+ db_field('id_ticket', 'int'), |
|
562 |
+ ), |
|
563 |
+ 'indexes' => array( |
|
564 |
+ array( |
|
565 |
+ 'columns' => array('id_word', 'id_ticket'), |
|
566 |
+ 'type' => 'primary', |
|
567 |
+ ), |
|
568 |
+ ), |
|
569 |
+ 'if_exists' => 'ignore', |
|
570 |
+ 'error' => 'fatal', |
|
571 |
+ 'parameters' => array(), |
|
572 |
+); |
|
539 | 573 |
|
540 | 574 |
// Oh joy, we've now made it to extra rows... |
541 | 575 |
$rows = array(); |
... | ... |
@@ -659,6 +693,12 @@ if (!empty($new_dept)) |
659 | 693 |
); |
660 | 694 |
} |
661 | 695 |
|
696 |
+// Do we need to flag that a new search index is needed? If there are any pre-existing tickets, we will... |
|
697 |
+$query = $smcFunc['db_query']('', 'SELECT COUNT(*) FROM {db_prefix}helpdesk_tickets'); |
|
698 |
+list($count) = $smcFunc['db_fetch_row']($query); |
|
699 |
+if (!empty($count)) |
|
700 |
+ updateSettings(array('shd_new_search_index' => 1)); |
|
701 |
+ |
|
662 | 702 |
// If we're updating an existing install, we need to make sure there is a normalised value in the last_updated column. |
663 | 703 |
$smcFunc['db_query']('', ' |
664 | 704 |
UPDATE {db_prefix}helpdesk_tickets AS hdt, {db_prefix}helpdesk_ticket_replies AS hdtr |
... | ... |
@@ -720,6 +760,14 @@ function db_field($name, $type, $size = 0, $unsigned = true, $auto = false) |
720 | 760 |
'unsigned' => $unsigned, |
721 | 761 |
'null' => false, |
722 | 762 |
), |
763 |
+ 'bigint' => array( |
|
764 |
+ 'auto' => $auto, |
|
765 |
+ 'type' => 'bigint', |
|
766 |
+ 'default' => 0, |
|
767 |
+ 'size' => 21, |
|
768 |
+ 'unsigned' => $unsigned, |
|
769 |
+ 'null' => false, |
|
770 |
+ ), |
|
723 | 771 |
); |
724 | 772 |
|
725 | 773 |
$field = $fields[$type]; |
... | ... |
@@ -168,6 +168,7 @@ $txt['shd_welcome'] = 'Welcome, %s!'; |
168 | 168 |
$txt['shd_go'] = 'Go!'; |
169 | 169 |
$txt['shd_go_to_ticket'] = 'Go to ticket'; |
170 | 170 |
$txt['shd_options'] = 'Options'; |
171 |
+$txt['shd_search_menu'] = 'Search'; |
|
171 | 172 |
// The strings that go into the menu... |
172 | 173 |
$txt['shd_admin_info'] = 'Information'; |
173 | 174 |
$txt['shd_admin_options'] = 'Options'; |
... | ... |
@@ -442,4 +443,38 @@ $txt['shd_ticket_notify_me_never'] = 'You have turned off all notifications for |
442 | 443 |
$txt['shd_ticket_notify_me_never_on'] = 'Turn off notifications'; |
443 | 444 |
$txt['shd_ticket_notify_me_never_off'] = 'Turn on notifications'; |
444 | 445 |
|
446 |
+// Searching |
|
447 |
+$txt['shd_search_warning_nonadmin'] = 'The search facility may not list all available tickets; it is currently being investigated.'; |
|
448 |
+$txt['shd_search_warning_admin'] = 'The search facility requires that its index be rebuilt. You can achieve this from the Maintenance option, in the Helpdesk area, in the administration panel.'; |
|
449 |
+$txt['shd_search'] = 'Search Tickets'; |
|
450 |
+$txt['shd_search_results'] = 'Search Tickets - Results'; |
|
451 |
+$txt['shd_search_text'] = 'Words you are looking for:'; |
|
452 |
+$txt['shd_search_match'] = 'What should be matched?'; |
|
453 |
+$txt['shd_search_match_all'] = 'Match all words supplied'; |
|
454 |
+$txt['shd_search_match_any'] = 'Match any words supplied'; |
|
455 |
+$txt['shd_search_scope'] = 'Include which types of tickets:'; |
|
456 |
+$txt['shd_search_scope_open'] = 'Open tickets'; |
|
457 |
+$txt['shd_search_scope_closed'] = 'Closed tickets'; |
|
458 |
+$txt['shd_search_scope_recycle'] = 'Items in the recycle bin'; |
|
459 |
+$txt['shd_search_result_ticket'] = 'Ticket %1$s'; |
|
460 |
+$txt['shd_search_result_reply'] = 'Reply to ticket %1$s'; |
|
461 |
+$txt['shd_search_last_updated'] = 'Last updated:'; |
|
462 |
+$txt['shd_search_ticket_opened_by'] = 'Ticket opened by:'; |
|
463 |
+$txt['shd_search_ticket_replied_by'] = 'Ticket replied to by:'; |
|
464 |
+$txt['shd_search_dept'] = 'Search in which department(s):'; |
|
465 |
+ |
|
466 |
+$txt['shd_search_urgency'] = 'Include which levels of urgency:'; |
|
467 |
+ |
|
468 |
+$txt['shd_search_where'] = 'Which items to search:'; |
|
469 |
+$txt['shd_search_where_tickets'] = 'The bodies of tickets'; |
|
470 |
+$txt['shd_search_where_replies'] = 'The replies in tickets'; |
|
471 |
+$txt['shd_search_where_subjects'] = 'Ticket subjects'; |
|
472 |
+ |
|
473 |
+$txt['shd_search_ticket_starter'] = 'Tickets started by:'; |
|
474 |
+$txt['shd_search_ticket_assignee'] = 'Tickets assigned to:'; |
|
475 |
+$txt['shd_search_ticket_named_person'] = 'Type in the name of the person(s) you are interested in.'; |
|
476 |
+ |
|
477 |
+$txt['shd_search_no_results'] = 'No results were found with the given criteria. You may wish to go back and try altering your search criteria.'; |
|
478 |
+$txt['shd_search_criteria'] = 'Search Criteria:'; |
|
479 |
+$txt['shd_search_excluded'] = 'If every possible option was selected, it has not been included in the above (e.g. if all possible levels of urgency were ticked, it is not stated above, so you can concentrate on what is specific to your search)'; |
|
445 | 480 |
?> |
446 | 481 |
\ No newline at end of file |
... | ... |
@@ -558,6 +558,18 @@ $txt['shd_maint_first_last'] = '%1$d ticket(s) had incorrect messages flagged fo |
558 | 558 |
$txt['shd_maint_status'] = '%1$d ticket(s) had the wrong status set for them. All have been rectified.'; |
559 | 559 |
$txt['shd_maint_starter_updater'] = '%1$d ticket(s) had the wrong user listed as the person who opened the ticket or the last person to update the ticket. All have been rectified.'; |
560 | 560 |
$txt['shd_maint_invalid_dept'] = '%1$d ticket(s) were listed as being in departments that do not exist, all were moved to a new department entitled "Recovered Tickets".'; |
561 |
+ |
|
562 |
+$txt['shd_maint_search_settings'] = 'Search Settings'; |
|
563 |
+$txt['shd_maint_search_settings_desc'] = 'This page allows you to configure how ticket searching may be performed, and if necessary, rebuild the index used to perform searching.'; |
|
564 |
+$txt['shd_maint_rebuild_index'] = 'Rebuild the Search Index'; |
|
565 |
+$txt['shd_maint_rebuild_index_desc'] = 'If you have existing tickets that were around prior to the search facility being provided, or you alter the settings below, you will <strong>need</strong> to rebuild the index after. The index is what is physically used to search, and if the physical index setup is different to how searches are made, you will find searching very unrealiable.<br /><strong>Important:</strong> Building the search index is a very intensive task. It will take a while to carry out, during which time please leave this window open.'; |
|
566 |
+$txt['shd_maint_search_settings_warning'] = 'If you alter these settings, you will need to rebuild the search index.'; |
|
567 |
+$txt['shd_search_min_size'] = 'Minimum number of letters to be considered a word (3-15)'; |
|
568 |
+$txt['shd_search_max_size'] = 'Maximum number of letters to be considered a word (3-15)'; |
|
569 |
+$txt['shd_search_prefix_size'] = 'Minimum number of letters to use for prefix searching<div class="smalltext">(0 = disabled)</div>'; |
|
570 |
+$txt['shd_search_prefix_size_help'] = 'Prefix searching is where the index is built to allow for partial word matches. For example, searching for "walk" will return results such as "walking" or "walked". It is disabled by default because it makes the index significantly bigger and searches do get slower as a consequence.'; |
|
571 |
+$txt['shd_search_charset'] = 'Characters to consider as valid parts of words to search.'; |
|
572 |
+$txt['shd_search_rebuilt'] = 'The search index has been rebuilt.'; |
|
561 | 573 |
//@} |
562 | 574 |
|
563 | 575 |
/** |
... | ... |
@@ -275,6 +275,12 @@ $txt['permissionname_shd_view_ip_own'] = 'Only their own'; |
275 | 275 |
$txt['permissionname_shd_view_ip_any'] = 'Anyone\'s'; |
276 | 276 |
//@} |
277 | 277 |
|
278 |
+//! @name Search |
|
279 |
+//@{ |
|
280 |
+$txt['permissionname_shd_search'] = 'Search tickets'; |
|
281 |
+$txt['permissionhelp_shd_search'] = 'This permission allows users to search tickets, it will be limited to tickets they can see.'; |
|
282 |
+//@} |
|
283 |
+ |
|
278 | 284 |
//! @name User profile access |
279 | 285 |
//@{ |
280 | 286 |
$txt['permissionname_shd_view_profile'] = 'View helpdesk profiles'; |
... | ... |
@@ -35,7 +35,7 @@ if (!defined('SMF')) |
35 | 35 |
*/ |
36 | 36 |
function shd_admin_maint() |
37 | 37 |
{ |
38 |
- global $context, $txt, $db_show_debug; |
|
38 |
+ global $context, $txt, $db_show_debug, $settings; |
|
39 | 39 |
|
40 | 40 |
// Right, if we're here, we really, really need to turn this off. Because anything we do from this page onwards hurts the log badly. |
41 | 41 |
$db_show_debug = false; |
... | ... |
@@ -45,14 +45,55 @@ function shd_admin_maint() |
45 | 45 |
loadLanguage('ManageMaintenance'); |
46 | 46 |
|
47 | 47 |
$subactions = array( |
48 |
- 'main' => 'shd_admin_maint_home', |
|
49 |
- 'reattribute' => 'shd_admin_maint_reattribute', |
|
50 |
- 'massdeptmove' => 'shd_admin_maint_massdeptmove', |
|
51 |
- 'findrepair' => 'shd_admin_maint_findrepair', |
|
48 |
+ 'main' => array( |
|
49 |
+ 'function' => 'shd_admin_maint_home', |
|
50 |
+ 'icon' => 'maintenance.png', |
|
51 |
+ 'title' => $txt['shd_admin_maint'], |
|
52 |
+ ), |
|
53 |
+ 'reattribute' => array( |
|
54 |
+ 'function' => 'shd_admin_maint_reattribute', |
|
55 |
+ 'icon' => 'user.png', |
|
56 |
+ 'title' => $txt['shd_admin_maint_reattribute'], |
|
57 |
+ 'description' => $txt['shd_admin_maint_reattribute_desc'], |
|
58 |
+ ), |
|
59 |
+ 'massdeptmove' => array( |
|
60 |
+ 'function' => 'shd_admin_maint_massdeptmove', |
|
61 |
+ 'icon' => 'movedept.png', |
|
62 |
+ 'title' => $txt['shd_admin_maint_massdeptmove'], |
|
63 |
+ 'description' => $txt['shd_admin_maint_massdeptmove'], |
|
64 |
+ ), |
|
65 |
+ 'findrepair' => array( |
|
66 |
+ 'function' => 'shd_admin_maint_findrepair', |
|
67 |
+ 'icon' => 'find_repair.png', |
|
68 |
+ 'title' => $txt['shd_admin_maint_findrepair'], |
|
69 |
+ 'description' => $txt['shd_admin_maint_findrepair_desc'], |
|
70 |
+ ), |
|
71 |
+ 'search' => array( |
|
72 |
+ 'function' => 'shd_admin_maint_search', |
|
73 |
+ 'icon' => 'search.png', |
|
74 |
+ 'title' => $txt['shd_maint_search_settings'], |
|
75 |
+ ), |
|
52 | 76 |
); |
53 | 77 |
|
54 | 78 |
$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subactions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'main'; |
55 |
- $subactions[$_REQUEST['sa']](); |
|
79 |
+ |
|
80 |
+ $context[$context['admin_menu_name']]['tab_data'] = array( |
|
81 |
+ 'title' => '<img src="' . $settings['default_theme_url'] . '/images/simpledesk/' . $subactions[$_REQUEST['sa']]['icon'] . '" class="icon" alt="*" />' . $subactions[$_REQUEST['sa']]['title'], |
|
82 |
+ 'description' => $txt['shd_admin_options_desc'], |
|
83 |
+ 'tabs' => array( |
|
84 |
+ 'main' => array( |
|
85 |
+ 'description' => $txt['shd_admin_maint_desc'], |
|
86 |
+ ), |
|
87 |
+ 'search' => array( |
|
88 |
+ 'description' => $txt['shd_maint_search_settings_desc'], |
|
89 |
+ ), |
|
90 |
+ ), |
|
91 |
+ ); |
|
92 |
+ |
|
93 |
+ // We need to fix the descriptions just in case. |
|
94 |
+ if (isset($subactions[$_REQUEST['sa']]['description'])) |
|
95 |
+ $context[$context['admin_menu_name']]['tab_data']['tabs']['main']['description'] = $subactions[$_REQUEST['sa']]['description']; |
|
96 |
+ $subactions[$_REQUEST['sa']]['function'](); |
|
56 | 97 |
} |
57 | 98 |
|
58 | 99 |
function shd_admin_maint_home() |
... | ... |
@@ -521,7 +562,7 @@ function shd_maint_deleted() |
521 | 562 |
{ |
522 | 563 |
// More to do, call back - and provide the subtitle |
523 | 564 |
$context['continue_post_data'] .= '<input type="hidden" name="step" value="' . $context['step'] . '" /> |
524 |
- <input type="hidden" name="start" value="' . $_REQUEST['start'] . '">'; |
|
565 |
+ <input type="hidden" name="start" value="' . $_REQUEST['start'] . '" />'; |
|
525 | 566 |
$context['substep_enabled'] = true; |
526 | 567 |
$context['substep_title'] = $txt['shd_admin_maint_findrepair_status']; |
527 | 568 |
$context['substep_continue_percent'] = round(100 * $_REQUEST['start'] / $ticket_count); |
... | ... |
@@ -612,7 +653,7 @@ function shd_maint_first_last() |
612 | 653 |
{ |
613 | 654 |
// More to do, call back - and provide the subtitle |
614 | 655 |
$context['continue_post_data'] .= '<input type="hidden" name="step" value="' . $context['step'] . '" /> |
615 |
- <input type="hidden" name="start" value="' . $_REQUEST['start'] . '">'; |
|
656 |
+ <input type="hidden" name="start" value="' . $_REQUEST['start'] . '" />'; |
|
616 | 657 |
$context['substep_enabled'] = true; |
617 | 658 |
$context['substep_title'] = $txt['shd_admin_maint_findrepair_firstlast']; |
618 | 659 |
$context['substep_continue_percent'] = round(100 * $_REQUEST['start'] / $ticket_count); |
... | ... |
@@ -703,7 +744,7 @@ function shd_maint_starter_updater() |
703 | 744 |
{ |
704 | 745 |
// More to do, call back - and provide the subtitle |
705 | 746 |
$context['continue_post_data'] .= '<input type="hidden" name="step" value="' . $context['step'] . '" /> |
706 |
- <input type="hidden" name="start" value="' . $_REQUEST['start'] . '">'; |
|
747 |
+ <input type="hidden" name="start" value="' . $_REQUEST['start'] . '" />'; |
|
707 | 748 |
$context['substep_enabled'] = true; |
708 | 749 |
$context['substep_title'] = $txt['shd_admin_maint_findrepair_starterupdater']; |
709 | 750 |
$context['substep_continue_percent'] = round(100 * $_REQUEST['start'] / $ticket_count); |
... | ... |
@@ -790,7 +831,7 @@ function shd_maint_status() |
790 | 831 |
{ |
791 | 832 |
// More to do, call back - and provide the subtitle |
792 | 833 |
$context['continue_post_data'] .= '<input type="hidden" name="step" value="' . $context['step'] . '" /> |
793 |
- <input type="hidden" name="start" value="' . $_REQUEST['start'] . '">'; |
|
834 |
+ <input type="hidden" name="start" value="' . $_REQUEST['start'] . '" />'; |
|
794 | 835 |
$context['substep_enabled'] = true; |
795 | 836 |
$context['substep_title'] = $txt['shd_admin_maint_findrepair_firstlast']; |
796 | 837 |
$context['substep_continue_percent'] = round(100 * $_REQUEST['start'] / $ticket_count); |
... | ... |
@@ -863,4 +904,223 @@ function shd_maint_clean_cache() |
863 | 904 |
redirectexit('action=admin;area=helpdesk_maint;sa=findrepair;done;' . $context['session_var'] . '=' . $context['session_id']); |
864 | 905 |
} |
865 | 906 |
|
907 |
+function shd_admin_maint_search() |
|
908 |
+{ |
|
909 |
+ global $context, $txt, $modSettings, $sourcedir, $smcFunc; |
|
910 |
+ |
|
911 |
+ $context['sub_template'] = 'shd_admin_maint_search'; |
|
912 |
+ $context['page_title'] = $txt['shd_admin_maint']; |
|
913 |
+ |
|
914 |
+ checkSession('request'); |
|
915 |
+ |
|
916 |
+ // Reset the defaults if they're not set. |
|
917 |
+ if (empty($modSettings['shd_search_charset'])) |
|
918 |
+ $modSettings['shd_search_charset'] = '0..9, A..Z, a..z, &, ~'; |
|
919 |
+ |
|
920 |
+ $modSettings['shd_search_min_size'] = !empty($modSettings['shd_search_min_size']) ? $modSettings['shd_search_min_size'] : 3; |
|
921 |
+ $modSettings['shd_search_max_size'] = !empty($modSettings['shd_search_max_size']) ? $modSettings['shd_search_max_size'] : 8; |
|
922 |
+ $modSettings['shd_search_prefix_size'] = !empty($modSettings['shd_search_prefix_size']) ? $modSettings['shd_search_prefix_size'] : 0; |
|
923 |
+ |
|
924 |
+ // Are we doing some fancy work? |
|
925 |
+ if (isset($_REQUEST['rebuild'])) |
|
926 |
+ { |
|
927 |
+ require_once($sourcedir . '/sd_source/Subs-SimpleDeskSearch.php'); |
|
928 |
+ // How many tickets are there? |
|
929 |
+ $query = $smcFunc['db_query']('', ' |
|
930 |
+ SELECT COUNT(id_ticket) |
|
931 |
+ FROM {db_prefix}helpdesk_tickets'); |
|
932 |
+ list($total) = $smcFunc['db_fetch_row']($query); |
|
933 |
+ |
|
934 |
+ // Where are we starting? |
|
935 |
+ $start = isset($_POST['start']) ? (int) $_POST['start'] : 0; |
|
936 |
+ |
|
937 |
+ // Get the ids we need to do. |
|
938 |
+ $per_inst = 10; |
|
939 |
+ $tickets = array(); |
|
940 |
+ $query = $smcFunc['db_query']('', ' |
|
941 |
+ SELECT id_ticket, subject |
|
942 |
+ FROM {db_prefix}helpdesk_tickets |
|
943 |
+ ORDER BY id_ticket ASC |
|
944 |
+ LIMIT {int:start}, {int:limit}', |
|
945 |
+ array( |
|
946 |
+ 'start' => $start, |
|
947 |
+ 'limit' => $per_inst, |
|
948 |
+ ) |
|
949 |
+ ); |
|
950 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
951 |
+ $tickets[$row['id_ticket']] = $row['subject']; |
|
952 |
+ $smcFunc['db_free_result']($query); |
|
953 |
+ |
|
954 |
+ // Nothing to do? |
|
955 |
+ if ($start >= $total || empty($tickets)) |
|
956 |
+ { |
|
957 |
+ // Make sure we flag the index as built, then leave. |
|
958 |
+ updateSettings( |
|
959 |
+ array( |
|
960 |
+ 'shd_new_search_index' => 0, |
|
961 |
+ ) |
|
962 |
+ ); |
|
963 |
+ redirectexit('action=admin;area=helpdesk_maint;sa=search;rebuilddone;' . $context['session_var'] . '=' . $context['session_id']); |
|
964 |
+ } |
|
965 |
+ |
|
966 |
+ // OK, let's get cracking. First, remove the relevant tickets from the subject index. |
|
967 |
+ $smcFunc['db_query']('', ' |
|
968 |
+ DELETE FROM {db_prefix}helpdesk_search_subject_words |
|
969 |
+ WHERE id_ticket IN ({array_int:tickets})', |
|
970 |
+ array( |
|
971 |
+ 'tickets' => array_keys($tickets), |
|
972 |
+ ) |
|
973 |
+ ); |
|
974 |
+ |
|
975 |
+ // Now, figure out the new term index for the subjects. |
|
976 |
+ $rows_to_insert = array(); |
|
977 |
+ foreach ($tickets as $id_ticket => $subject) |
|
978 |
+ { |
|
979 |
+ $tokens = shd_tokeniser($subject); |
|
980 |
+ foreach ($tokens as $token) |
|
981 |
+ $rows_to_insert[] = array($token, $id_ticket); |
|
982 |
+ } |
|
983 |
+ |
|
984 |
+ // And add to the database. |
|
985 |
+ if (!empty($rows_to_insert)) |
|
986 |
+ $smcFunc['db_insert']('replace', |
|
987 |
+ '{db_prefix}helpdesk_search_subject_words', |
|
988 |
+ array('id_word' => 'string', 'id_ticket' => 'int'), |
|
989 |
+ $rows_to_insert, |
|
990 |
+ array('id_word', 'id_ticket') |
|
991 |
+ ); |
|
992 |
+ |
|
993 |
+ // Now for the slightly... substantially more expensive part: messages. We query for all the messages in a ticket, then query to |
|
994 |
+ // insert all the terms for each message. Expensive since it means a lot of queries but it means we don't risk hitting the query |
|
995 |
+ // packet limit which could really break things. Besides, this IS a maintenance area, not something you're going to do that often. |
|
996 |
+ foreach ($tickets as $id_ticket => $subject) |
|
997 |
+ { |
|
998 |
+ $rows_to_insert = array(); |
|
999 |
+ $query = $smcFunc['db_query']('', ' |
|
1000 |
+ SELECT id_msg, body |
|
1001 |
+ FROM {db_prefix}helpdesk_ticket_replies |
|
1002 |
+ WHERE id_ticket = {int:ticket}', |
|
1003 |
+ array( |
|
1004 |
+ 'ticket' => $id_ticket, |
|
1005 |
+ ) |
|
1006 |
+ ); |
|
1007 |
+ $msg_list = array(); |
|
1008 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
1009 |
+ { |
|
1010 |
+ $msg_list[] = $row['id_msg']; |
|
1011 |
+ $tokens = shd_tokeniser($row['body']); |
|
1012 |
+ foreach ($tokens as $token) |
|
1013 |
+ $rows_to_insert[] = array($token, $row['id_msg']); |
|
1014 |
+ } |
|
1015 |
+ $smcFunc['db_free_result']($query); |
|
1016 |
+ |
|
1017 |
+ // Just before we insert, prune the old stuff. No point querying the message list twice. |
|
1018 |
+ $smcFunc['db_query']('', ' |
|
1019 |
+ DELETE FROM {db_prefix}helpdesk_search_ticket_words |
|
1020 |
+ WHERE id_msg IN ({array_int:msgs})', |
|
1021 |
+ array( |
|
1022 |
+ 'msgs' => $msg_list, |
|
1023 |
+ ) |
|
1024 |
+ ); |
|
1025 |
+ |
|
1026 |
+ if (!empty($rows_to_insert)) |
|
1027 |
+ $smcFunc['db_insert']('replace', |
|
1028 |
+ '{db_prefix}helpdesk_search_ticket_words', |
|
1029 |
+ array('id_word' => 'string', 'id_msg' => 'int'), |
|
1030 |
+ $rows_to_insert, |
|
1031 |
+ array('id_word', 'id_msg') |
|
1032 |
+ ); |
|
1033 |
+ } |
|
1034 |
+ |
|
1035 |
+ // Set up for calling back. |
|
1036 |
+ $start += $per_inst; |
|
1037 |
+ $pc_done = round($start / $total * 100); |
|
1038 |
+ if ($pc_done > 100) |
|
1039 |
+ $pc_done = 100; |
|
1040 |
+ |
|
1041 |
+ $context['continue_countdown'] = 3; |
|
1042 |
+ $context['sub_template'] = 'not_done'; |
|
1043 |
+ $context['continue_percent'] = $pc_done; |
|
1044 |
+ $context['continue_get_data'] = '?action=admin;area=helpdesk_maint;sa=search;' . $context['session_var'] . '=' . $context['session_id']; |
|
1045 |
+ $context['continue_post_data'] = '<input type="hidden" name="start" value="' . $start . '" /> |
|
1046 |
+ <input type="hidden" name="rebuild" value="1" />'; |
|
1047 |
+ |
|
1048 |
+ // Make SURE we never mess with the other settings. |
|
1049 |
+ unset($_REQUEST['save']); |
|
1050 |
+ } |
|
1051 |
+ |
|
1052 |
+ // OK, the template will basically display itself, but in the meantime, do we need to do anything else like save new settings? |
|
1053 |
+ if (isset($_REQUEST['save'])) |
|
1054 |
+ { |
|
1055 |
+ $_POST['shd_search_min_size'] = isset($_POST['shd_search_min_size']) ? (int) $_POST['shd_search_min_size'] : 0; |
|
1056 |
+ $_POST['shd_search_max_size'] = isset($_POST['shd_search_max_size']) ? (int) $_POST['shd_search_max_size'] : 0; |
|
1057 |
+ $_POST['shd_search_prefix_size'] = isset($_POST['shd_search_prefix_size']) ? (int) $_POST['shd_search_prefix_size'] : 0; |
|
1058 |
+ |
|
1059 |
+ // Force some realistic limits. |
|
1060 |
+ if ($_POST['shd_search_min_size'] < 3) |
|
1061 |
+ $_POST['shd_search_min_size'] = 3; |
|
1062 |
+ elseif ($_POST['shd_search_min_size'] > 15) |
|
1063 |
+ $_POST['shd_search_min_size'] = 15; |
|
1064 |
+ |
|
1065 |
+ if ($_POST['shd_search_max_size'] < $_POST['shd_search_min_size']) |
|
1066 |
+ $_POST['shd_search_max_size'] = $_POST['shd_search_min_size']; |
|
1067 |
+ elseif ($_POST['shd_search_max_size'] > 15) |
|
1068 |
+ $_POST['shd_search_max_size'] = 15; |
|
1069 |
+ |
|
1070 |
+ if ($_POST['shd_search_prefix_size'] < 0) |
|
1071 |
+ $_POST['shd_search_prefix_size'] = 0; |
|
1072 |
+ elseif ($_POST['shd_search_prefix_size'] > 0 && $_POST['shd_search_prefix_size'] < $_POST['shd_search_min_size']) |
|
1073 |
+ $_POST['shd_search_prefix_size'] = $_POST['shd_search_min_size']; |
|
1074 |
+ elseif ($_POST['shd_search_prefix_size'] > $_POST['shd_search_max_size']) |
|
1075 |
+ $_POST['shd_search_prefix_size'] = $_POST['shd_search_max_size']; |
|
1076 |
+ |
|
1077 |
+ $normal_regex = shd_return_exclude_regex($modSettings['shd_search_charset']); |
|
1078 |
+ if (empty($_POST['shd_search_charset'])) |
|
1079 |
+ $_POST['shd_search_charset'] = $modSettings['shd_search_charset']; |
|
1080 |
+ $post_regex = shd_return_exclude_regex($_POST['shd_search_charset']); |
|
1081 |
+ if (empty($post_regex)) |
|
1082 |
+ $post_regex = $normal_regex; // Nothing specified? Use what we have, then. |
|
1083 |
+ |
|
1084 |
+ foreach (array('shd_search_min_size', 'shd_search_max_size', 'shd_search_prefix_size') as $item) |
|
1085 |
+ if ($modSettings[$item] != $_POST[$item]) |
|
1086 |
+ $update = true; |
|
1087 |
+ |
|
1088 |
+ if ($normal_regex != $post_regex) |
|
1089 |
+ $update = true; |
|
1090 |
+ |
|
1091 |
+ if (!empty($update)) |
|
1092 |
+ updateSettings( |
|
1093 |
+ array( |
|
1094 |
+ 'shd_search_min_size' => $_POST['shd_search_min_size'], |
|
1095 |
+ 'shd_search_max_size' => $_POST['shd_search_max_size'], |
|
1096 |
+ 'shd_search_prefix_size' => $_POST['shd_search_prefix_size'], |
|
1097 |
+ 'shd_search_charset' => $_POST['shd_search_charset'], |
|
1098 |
+ 'shd_new_search_index' => 1, |
|
1099 |
+ ) |
|
1100 |
+ ); |
|
1101 |
+ } |
|
1102 |
+} |
|
1103 |
+ |
|
1104 |
+// This uses the same methodology as Subs-SimpleDeskSearch.php's shd_search_charset routine. |
|
1105 |
+function shd_return_exclude_regex($source) |
|
1106 |
+{ |
|
1107 |
+ global $context; |
|
1108 |
+ |
|
1109 |
+ $terms = explode(',', $source); |
|
1110 |
+ $exclude_regex = ''; |
|
1111 |
+ foreach ($terms as $k => $v) |
|
1112 |
+ { |
|
1113 |
+ $v = trim($v); |
|
1114 |
+ if (preg_match('~^(.)$~i' . ($context['utf8'] ? 'u' : ''), $v, $match)) // Single character |
|
1115 |
+ $exclude_regex .= preg_quote($match[1], '~'); |
|
1116 |
+ elseif (preg_match('~^(.)\.\.(.)$~i' . ($context['utf8'] ? 'u' : ''), $v, $match)) // It's a ranged component. |
|
1117 |
+ $exclude_regex .= preg_quote($match[1], '~') . '-' . preg_quote($match[2], '~'); |
|
1118 |
+ } |
|
1119 |
+ if (empty($exclude_regex)) |
|
1120 |
+ $exclude_regex = ''; |
|
1121 |
+ else |
|
1122 |
+ $exclude_regex = '~[^' . $exclude_regex . ']+~' . ($context['utf8'] ? 'u' : ''); |
|
1123 |
+ |
|
1124 |
+ return $exclude_regex; |
|
1125 |
+} |
|
866 | 1126 |
?> |
867 | 1127 |
\ No newline at end of file |
... | ... |
@@ -200,6 +200,11 @@ function shd_perma_delete() |
200 | 200 |
|
201 | 201 |
checkSession('get'); |
202 | 202 |
|
203 |
+ // This is heavy duty stuff. |
|
204 |
+ @set_time_limit(0); |
|
205 |
+ if (is_callable('apache_reset_timeout')) |
|
206 |
+ apache_reset_timeout(); |
|
207 |
+ |
|
203 | 208 |
// We have to have either a ticket or a reply to know what to delete (Or do you want me to drop your whole database? >:D) |
204 | 209 |
if (empty($context['ticket_id']) && empty($_REQUEST['reply'])) |
205 | 210 |
fatal_lang_error('shd_no_ticket', false); |
... | ... |
@@ -294,6 +299,23 @@ function shd_perma_delete() |
294 | 299 |
) |
295 | 300 |
); |
296 | 301 |
|
302 |
+ // And search entries. |
|
303 |
+ shd_db_query('', ' |
|
304 |
+ DELETE FROM {db_prefix}helpdesk_search_ticket_words |
|
305 |
+ WHERE id_msg = ({array_int:msgs})', |
|
306 |
+ array( |
|
307 |
+ 'msgs' => $msgs, |
|
308 |
+ ) |
|
309 |
+ ); |
|
310 |
+ |
|
311 |
+ shd_db_query('', ' |
|
312 |
+ DELETE FROM {db_prefix}helpdesk_search_subject_words |
|
313 |
+ WHERE id_ticket = {int:ticket}', |
|
314 |
+ array( |
|
315 |
+ 'current_ticket' => $context['ticket_id'], |
|
316 |
+ ) |
|
317 |
+ ); |
|
318 |
+ |
|
297 | 319 |
// And attachments... work out which attachments that is |
298 | 320 |
$query = shd_db_query('', ' |
299 | 321 |
SELECT id_attach |
... | ... |
@@ -392,6 +414,15 @@ function shd_perma_delete() |
392 | 414 |
) |
393 | 415 |
); |
394 | 416 |
|
417 |
+ // And search entries. |
|
418 |
+ shd_db_query('', ' |
|
419 |
+ DELETE FROM {db_prefix}helpdesk_search_ticket_words |
|
420 |
+ WHERE id_msg = {int:reply}', |
|
421 |
+ array( |
|
422 |
+ 'reply' => (int) $_REQUEST['reply'], |
|
423 |
+ ) |
|
424 |
+ ); |
|
425 |
+ |
|
395 | 426 |
// Now to handle attachments |
396 | 427 |
$query = shd_db_query('', ' |
397 | 428 |
SELECT id_attach |
... | ... |
@@ -0,0 +1,510 @@ |
1 |
+<?php |
|
2 |
+############################################################### |
|
3 |
+# Simple Desk Project - www.simpledesk.net # |
|
4 |
+############################################################### |
|
5 |
+# An advanced help desk modifcation built on SMF # |
|
6 |
+############################################################### |
|
7 |
+# # |
|
8 |
+# * Copyright 2010 - SimpleDesk.net # |
|
9 |
+# # |
|
10 |
+# This file and its contents are subject to the license # |
|
11 |
+# included with this distribution, license.txt, which # |
|
12 |
+# states that this software is New BSD Licensed. # |
|
13 |
+# Any questions, please contact SimpleDesk.net # |
|
14 |
+# # |
|
15 |
+############################################################### |
|
16 |
+# SimpleDesk Version: 2.0 Anatidae # |
|
17 |
+# File Info: SimpleDesk-Search.php / 2.0 Anatidae # |
|
18 |
+############################################################### |
|
19 |
+ |
|
20 |
+/** |
|
21 |
+ * This file handles searching, providing the interface and handling. |
|
22 |
+ * |
|
23 |
+ * @package source |
|
24 |
+ * @since 2.0 |
|
25 |
+*/ |
|
26 |
+ |
|
27 |
+if (!defined('SMF')) |
|
28 |
+ die('Hacking attempt...'); |
|
29 |
+ |
|
30 |
+function shd_search() |
|
31 |
+{ |
|
32 |
+ global $context, $smcFunc, $txt, $modSettings, $scripturl; |
|
33 |
+ |
|
34 |
+ shd_is_allowed_to('shd_search', 0); |
|
35 |
+ |
|
36 |
+ if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) |
|
37 |
+ fatal_lang_error('loadavg_search_disabled', false); |
|
38 |
+ |
|
39 |
+ loadTemplate('sd_template/SimpleDesk-Search'); |
|
40 |
+ |
|
41 |
+ $visible_depts = shd_allowed_to('access_helpdesk', false); |
|
42 |
+ $context['dept_list'] = array(); |
|
43 |
+ $query = $smcFunc['db_query']('', ' |
|
44 |
+ SELECT id_dept, dept_name |
|
45 |
+ FROM {db_prefix}helpdesk_depts |
|
46 |
+ WHERE id_dept IN ({array_int:depts}) |
|
47 |
+ ORDER BY dept_order', |
|
48 |
+ array( |
|
49 |
+ 'depts' => $visible_depts, |
|
50 |
+ ) |
|
51 |
+ ); |
|
52 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
53 |
+ $context['dept_list'][$row['id_dept']] = $row['dept_name']; |
|
54 |
+ $smcFunc['db_free_result']($query); |
|
55 |
+ |
|
56 |
+ $context['sub_template'] = 'search'; |
|
57 |
+ $context['page_title'] = $txt['shd_search']; |
|
58 |
+ |
|
59 |
+ $context['linktree'][] = array( |
|
60 |
+ 'url' => $scripturl . '?action=helpdesk;sa=search', |
|
61 |
+ 'name' => $txt['shd_search'], |
|
62 |
+ ); |
|
63 |
+} |
|
64 |
+ |
|
65 |
+function shd_search2() |
|
66 |
+{ |
|
67 |
+ global $context, $smcFunc, $txt, $modSettings, $scripturl, $sourcedir; |
|
68 |
+ |
|
69 |
+ shd_is_allowed_to('shd_search', 0); |
|
70 |
+ |
|
71 |
+ if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) |
|
72 |
+ fatal_lang_error('loadavg_search_disabled', false); |
|
73 |
+ |
|
74 |
+ // No, no, no... this is a bit hard on the server, so don't you go prefetching it! |
|
75 |
+ if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') |
|
76 |
+ { |
|
77 |
+ ob_end_clean(); |
|
78 |
+ header('HTTP/1.1 403 Forbidden'); |
|
79 |
+ die; |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ // We will need this. |
|
83 |
+ require_once($sourcedir . '/sd_source/Subs-SimpleDeskSearch.php'); |
|
84 |
+ |
|
85 |
+ loadTemplate('sd_template/SimpleDesk-Search'); |
|
86 |
+ $context['page_title'] = $txt['shd_search_results']; |
|
87 |
+ |
|
88 |
+ $context['linktree'][] = array( |
|
89 |
+ 'name' => $txt['shd_search_results'], |
|
90 |
+ ); |
|
91 |
+ |
|
92 |
+ $context['search_clauses'] = array('{query_see_ticket}'); |
|
93 |
+ $context['search_params'] = array(); |
|
94 |
+ |
|
95 |
+ // Departments first. |
|
96 |
+ $visible_depts = shd_allowed_to('access_helpdesk', false); |
|
97 |
+ $using_depts = array(); |
|
98 |
+ if (!empty($_POST['search_dept']) && is_array($_POST['search_dept'])) |
|
99 |
+ foreach ($_POST['search_dept'] as $dept) |
|
100 |
+ if ((int) $dept > 0) |
|
101 |
+ $using_depts[] = (int) $dept; |
|
102 |
+ if (!empty($using_depts)) |
|
103 |
+ $using_depts = array_intersect($using_depts, $visible_depts); |
|
104 |
+ // No departments? Can't really do a lot, sorry. Bye then. |
|
105 |
+ if (empty($using_depts)) |
|
106 |
+ return $context['sub_template'] = 'search_no_results'; |
|
107 |
+ |
|
108 |
+ // Is the selected list the same size as the list we can see? If it is, theory says that means we're picking every department we can see and don't need to exclude it. |
|
109 |
+ if (count($using_depts) != count($visible_depts)) |
|
110 |
+ { |
|
111 |
+ $context['search_clauses'][] = 'hdt.id_dept IN ({array_int:visible_depts})'; |
|
112 |
+ $context['search_params']['visible_depts'] = $using_depts; |
|
113 |
+ |
|
114 |
+ // Also, we need to get the department list for displaying, only if we can actually see multiple departments at all. |
|
115 |
+ if ($context['shd_multi_dept']) |
|
116 |
+ { |
|
117 |
+ $query = $smcFunc['db_query']('', ' |
|
118 |
+ SELECT id_dept, dept_name |
|
119 |
+ FROM {db_prefix}helpdesk_depts |
|
120 |
+ WHERE id_dept IN ({array_int:dept_list}) |
|
121 |
+ ORDER BY dept_order', |
|
122 |
+ array( |
|
123 |
+ 'dept_list' => $using_depts, |
|
124 |
+ ) |
|
125 |
+ ); |
|
126 |
+ $context['search_dept_list'] = array(); |
|
127 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
128 |
+ $context['search_dept_list'][$row['id_dept']] = $row['dept_name']; |
|
129 |
+ $smcFunc['db_free_result']($query); |
|
130 |
+ } |
|
131 |
+ } |
|
132 |
+ |
|
133 |
+ // Ticket urgency |
|
134 |
+ $using_urgency = array(); |
|
135 |
+ if (!empty($_POST['urgency']) && is_array($_POST['urgency'])) |
|
136 |
+ foreach ($_POST['urgency'] as $urgency) |
|
137 |
+ { |
|
138 |
+ $urgency = (int) $urgency; |
|
139 |
+ if ($urgency >= 0 && $urgency <= 5) // All the currently defined urgencies |
|
140 |
+ $using_urgency[] = $urgency; |
|
141 |
+ } |
|
142 |
+ if (empty($using_urgency)) |
|
143 |
+ return $context['sub_template'] = 'search_no_results'; |
|
144 |
+ else |
|
145 |
+ { |
|
146 |
+ $using_urgency = array_unique($using_urgency); |
|
147 |
+ if (count($using_urgency) < 6) |
|
148 |
+ { |
|
149 |
+ // We have less than 6 selected urgencies, which means we actually need to filter on them, as opposed to if all 6 are selected when we don't. |
|
150 |
+ $context['search_clauses'][] = 'hdt.urgency IN ({array_int:urgency})'; |
|
151 |
+ $context['search_params']['urgency'] = $using_urgency; |
|
152 |
+ } |
|
153 |
+ } |
|
154 |
+ |
|
155 |
+ // Ticket scope |
|
156 |
+ // All empty? If so, bye. |
|
157 |
+ if (empty($_POST['scope_open']) && empty($_POST['scope_closed']) && empty($_POST['scope_recycle'])) |
|
158 |
+ return $context['sub_template'] = 'search_no_results'; |
|
159 |
+ // At least one empty? That way we have to do some filtering, you see. (All set means no filtering required.) |
|
160 |
+ elseif (empty($_POST['scope_open']) || empty($_POST['scope_closed']) || empty($_POST['scope_recycle'])) |
|
161 |
+ { |
|
162 |
+ $status = array(); |
|
163 |
+ if (!empty($_POST['scope_open'])) |
|
164 |
+ $status = array_merge($status, array(TICKET_STATUS_NEW, TICKET_STATUS_PENDING_STAFF, TICKET_STATUS_PENDING_USER, TICKET_STATUS_WITH_SUPERVISOR, TICKET_STATUS_ESCALATED)); |
|
165 |
+ if (!empty($_POST['scope_closed'])) |
|
166 |
+ $status = array_merge($status, array(TICKET_STATUS_CLOSED)); |
|
167 |
+ if (!empty($_POST['scope_recycle'])) |
|
168 |
+ $status = array_merge($status, array(TICKET_STATUS_DELETED)); |
|
169 |
+ |
|
170 |
+ $context['search_clauses'][] = 'hdt.status IN ({array_int:status})'; |
|
171 |
+ $context['search_params']['status'] = $status; |
|
172 |
+ |
|
173 |
+ // That's ticket level status taken care of. We'll pick up recycled items in non recycled tickets separately since it's only relevant if you're actually searching text. |
|
174 |
+ } |
|
175 |
+ |
|
176 |
+ // Ticket starter |
|
177 |
+ $starters = shd_get_named_people('starter'); |
|
178 |
+ if (!empty($starters)) |
|
179 |
+ { |
|
180 |
+ $context['search_clauses'][] = 'hdt.id_member_started IN ({array_int:member_started})'; |
|
181 |
+ $context['search_params']['member_started'] = $starters; |
|
182 |
+ } |
|
183 |
+ |
|
184 |
+ // Ticket assigned to |
|
185 |
+ $assignees = shd_get_named_people('assignee'); |
|
186 |
+ if (!empty($assignees)) |
|
187 |
+ { |
|
188 |
+ $context['search_clauses'][] = 'hdt.id_member_assigned IN ({array_int:member_assigned})'; |
|
189 |
+ $context['search_params']['member_assigned'] = $assignees; |
|
190 |
+ } |
|
191 |
+ |
|
192 |
+ // Lastly, page number. We're doing something different to SMF's normal style here. Long and complicated, but there you go. |
|
193 |
+ if (isset($_POST['page'])) |
|
194 |
+ $context['pagenum'] = (int) $_POST['page']; |
|
195 |
+ if (empty($context['pagenum']) || $context['pagenum'] < 1) |
|
196 |
+ $context['pagenum'] = 1; |
|
197 |
+ |
|
198 |
+ $number_per_page = 20; |
|
199 |
+ |
|
200 |
+ // OK, so are there any words? If not, execute this sucker the quick way and get out to the template quick. |
|
201 |
+ $context['search_terms'] = !empty($_POST['search']) ? trim($_POST['search']) : ''; |
|
202 |
+ |
|
203 |
+ // Also, did we select some text but fail to select what it was searching in? If so, kick it out. |
|
204 |
+ if (!empty($context['search_terms']) && empty($_POST['search_subjects']) && empty($_POST['search_tickets']) && empty($_POST['search_replies'])) |
|
205 |
+ return $context['sub_template'] = 'search_no_results'; |
|
206 |
+ elseif (!empty($context['search_terms'])) |
|
207 |
+ { |
|
208 |
+ // We're using search terms, and we need to store the areas we're covering. Only makes sense if we're using terms though. |
|
209 |
+ $context['search_params']['areas'] = array(); |
|
210 |
+ foreach (array('subjects', 'tickets', 'replies') as $area) |
|
211 |
+ if (!empty($_POST['search_' . $area])) |
|
212 |
+ $context['search_params']['areas'][$area] = true; |
|
213 |
+ |
|
214 |
+ // While we're at it, see if we actually have any words to search for. |
|
215 |
+ $tokens = shd_tokeniser($context['search_terms']); |
|
216 |
+ $count_tokens = count($tokens); |
|
217 |
+ |
|
218 |
+ // No actual words? |
|
219 |
+ if ($count_tokens == 0) |
|
220 |
+ { |
|
221 |
+ $context['search_terms'] = ''; |
|
222 |
+ unset($context['search_params']['areas']); |
|
223 |
+ } |
|
224 |
+ } |
|
225 |
+ |
|
226 |
+ // Spam me not! |
|
227 |
+ if (empty($_SESSION['lastsearch'])) |
|
228 |
+ spamProtection('search'); |
|
229 |
+ else |
|
230 |
+ { |
|
231 |
+ list($temp_clauses, $temp_params, $temp_terms) = unserialize($_SESSION['lastsearch']); |
|
232 |
+ if ($temp_clauses != $context['search_clauses'] || $temp_params != $context['search_params'] || $temp_terms != $context['search_terms']) |
|
233 |
+ spamProtection('search'); |
|
234 |
+ } |
|
235 |
+ $_SESSION['lastsearch'] = serialize(array($context['search_clauses'], $context['search_params'], $context['search_terms'])); |
|
236 |
+ |
|
237 |
+ $context['search_params']['start'] = ($context['pagenum'] - 1) * $number_per_page; |
|
238 |
+ $context['search_params']['limit'] = $number_per_page; |
|
239 |
+ |
|
240 |
+ if (empty($context['search_terms'])) |
|
241 |
+ { |
|
242 |
+ // This is where it starts to get expensive, *sob*. We first have to query to get the number of applicable rows. |
|
243 |
+ $query = shd_db_query('', ' |
|
244 |
+ SELECT COUNT(id_ticket) |
|
245 |
+ FROM {db_prefix}helpdesk_tickets AS hdt |
|
246 |
+ WHERE ' . implode(' AND ', $context['search_clauses']) . ' LIMIT 1000', |
|
247 |
+ $context['search_params'] |
|
248 |
+ ); |
|
249 |
+ list($count) = $smcFunc['db_fetch_row']($query); |
|
250 |
+ if ($count == 0) |
|
251 |
+ { |
|
252 |
+ $smcFunc['db_free_result']($query); |
|
253 |
+ return $context['sub_template'] = 'search_no_results'; |
|
254 |
+ } |
|
255 |
+ // OK, at least one result, awesome. Are we off the end of the list? |
|
256 |
+ if ($context['search_params']['start'] > $count) |
|
257 |
+ { |
|
258 |
+ $context['search_params']['start'] = $count - ($count % $number_per_page); |
|
259 |
+ $context['pagenum'] = ($context['search_params']['start'] / $number_per_page) + 1; |
|
260 |
+ $context['num_results'] = $count); |
|
261 |
+ } |
|
262 |
+ |
|
263 |
+ $query = shd_db_query('', ' |
|
264 |
+ SELECT hdt.id_ticket, hdt.id_dept, hdd.dept_name, hdt.subject, hdt.urgency, hdt.private, hdt.last_updated, hdtr.body, |
|
265 |
+ hdtr.smileys_enabled, hdtr.id_member AS id_member, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time |
|
266 |
+ FROM {db_prefix}helpdesk_tickets AS hdt |
|
267 |
+ INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_first_msg = hdtr.id_msg) |
|
268 |
+ INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept) |
|
269 |
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = hdtr.id_member) |
|
270 |
+ WHERE ' . implode(' AND ', $context['search_clauses']) . ' |
|
271 |
+ ORDER BY hdt.last_updated DESC |
|
272 |
+ LIMIT {int:start}, {int:limit}', |
|
273 |
+ $context['search_params'] |
|
274 |
+ ); |
|
275 |
+ |
|
276 |
+ $context['search_results'] = array(); |
|
277 |
+ $page_pos = $context['search_params']['start']; // e.g. 0 on page 1, 10 for page 2, the first item will be page_pos + 1, so ++ it before using it. |
|
278 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
279 |
+ { |
|
280 |
+ $row['result'] = ++$page_pos; // Increment first, then use. |
|
281 |
+ $row['display_id'] = str_pad($row['id_ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT); |
|
282 |
+ $row['is_ticket'] = true; // If we're here, we're only handling tickets anyway. If we're searching text we will need to know if it was a ticket or reply though. |
|
283 |
+ $row['dept_link'] = !$context['shd_multi_dept'] ? '' : '[<a href="' . $scripturl . '?action=helpdesk;sa=main;dept=' . $row['id_dept'] . '">' . $row['dept_name'] . '</a>] '; |
|
284 |
+ |
|
285 |
+ $context['search_results'][] = $row; |
|
286 |
+ } |
|
287 |
+ |
|
288 |
+ return $context['sub_template'] = 'search_results'; |
|
289 |
+ } |
|
290 |
+ else |
|
291 |
+ { |
|
292 |
+ $context['match_all'] = empty($_POST['searchtype']) || $_POST['searchtype'] == 'all'; |
|
293 |
+ |
|
294 |
+ // Then figure out what terms are being matched. |
|
295 |
+ $matches = array( |
|
296 |
+ 'subjects' => array(), |
|
297 |
+ 'messages' => array(), |
|
298 |
+ 'id_msg' => array(), |
|
299 |
+ ); |
|
300 |
+ |
|
301 |
+ // Doing subjects. Fetch all the instances that match and begin filtering as we go. |
|
302 |
+ if (!empty($context['search_params']['areas']['subjects'])) |
|
303 |
+ { |
|
304 |
+ $query = shd_db_query('', ' |
|
305 |
+ SELECT hdssw.id_word, hdt.id_first_msg |
|
306 |
+ FROM {db_prefix}helpdesk_search_subject_words AS hdssw |
|
307 |
+ INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdssw.id_ticket = hdt.id_ticket) |
|
308 |
+ WHERE {query_see_ticket} |
|
309 |
+ AND id_word IN ({array_string:tokens})', |
|
310 |
+ array( |
|
311 |
+ 'tokens' => $tokens, |
|
312 |
+ ) |
|
313 |
+ ); |
|
314 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
315 |
+ $matches['subjects'][$row['id_first_msg']][$row['id_word']] = true; |
|
316 |
+ $smcFunc['db_free_result']($query); |
|
317 |
+ |
|
318 |
+ // Now go through and figure out which tickets we're interested in keeping. |
|
319 |
+ if ($context['match_all']) |
|
320 |
+ foreach ($matches['subjects'] as $msg => $ticket_words) |
|
321 |
+ if (count($ticket_words) != $count_tokens) // How many words did we match in this subject? If it isn't the number we're expecting, ditch it. |
|
322 |
+ unset($matches['subjects'][$msg]); |
|
323 |
+ |
|
324 |
+ // Now, we just have a list of tickets to play with. Let's put that together in a master list. |
|
325 |
+ foreach ($matches['subjects'] as $msg => $ticket_words) |
|
326 |
+ $matches['id_msg'][$msg] = true; |
|
327 |
+ |
|
328 |
+ unset($matches['subjects']); |
|
329 |
+ } |
|
330 |
+ |
|
331 |
+ // Now we get the list of words that apply to tickets and replies. The process is different if we do one or both. Both, first. |
|
332 |
+ if (!empty($context['search_params']['areas']['tickets']) && !empty($context['search_params']['areas']['replies'])) |
|
333 |
+ { |
|
334 |
+ // If we're doing both replies and tickets themselves, we don't have to care too much about the message itself, except for being deleted. |
|
335 |
+ $query = shd_db_query('', ' |
|
336 |
+ SELECT hdssw.id_word, hdt.id_first_msg |
|
337 |
+ FROM {db_prefix}helpdesk_search_subject_words AS hdssw |
|
338 |
+ INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdssw.id_ticket = hdt.id_ticket) |
|
339 |
+ WHERE {query_see_ticket} |
|
340 |
+ AND id_word IN ({array_string:tokens})' . (empty($_POST['scope_recycle']) || !shd_allowed_to('shd_access_recyclebin', 0) ? ' |
|
341 |
+ AND hdtr.message_status = {int:not_deleted}' : ''), |
|
342 |
+ array( |
|
343 |
+ 'tokens' => $tokens, |
|
344 |
+ 'not_deleted' => MSG_STATUS_NORMAL, |
|
345 |
+ ) |
|
346 |
+ ); |
|
347 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
348 |
+ $matches['messages'][$row['id_first_msg']][$row['id_word']] = true; |
|
349 |
+ $smcFunc['db_free_result']($query); |
|
350 |
+ |
|
351 |
+ if ($context['match_all']) |
|
352 |
+ foreach ($matches['messages'] as $msg => $ticket_words) |
|
353 |
+ if (count($ticket_words) != $count_tokens) // How many words did we match in this subject? If it isn't the number we're expecting, ditch it. |
|
354 |
+ unset($matches['messages'][$msg]); |
|
355 |
+ |
|
356 |
+ // Now, we just have a list of tickets to play with. Let's put that together in a master list. |
|
357 |
+ foreach ($matches['messages'] as $msg => $ticket_words) |
|
358 |
+ $matches['id_msg'][$msg] = true; |
|
359 |
+ unset($matches['messages']); |
|
360 |
+ } |
|
361 |
+ // Just tickets OR replies |
|
362 |
+ elseif (!empty($context['search_params']['areas']['tickets']) || !empty($context['search_params']['areas']['replies'])) |
|
363 |
+ { |
|
364 |
+ $query = $smcFunc['db_query']('', ' |
|
365 |
+ SELECT hdstw.id_word, hdstw.id_msg |
|
366 |
+ FROM {db_prefix}helpdesk_search_ticket_words AS hdstw |
|
367 |
+ INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdstw.id_msg = hdtr.id_msg) |
|
368 |
+ INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket) |
|
369 |
+ WHERE id_word IN ({array_string:tokens}) |
|
370 |
+ AND hdstw.id_msg {raw:operator} hdt.id_first_msg' . (empty($_POST['scope_recycle']) || !shd_allowed_to('shd_access_recyclebin', 0) ? ' |
|
371 |
+ AND hdtr.message_status = {int:not_deleted}' : ''), |
|
372 |
+ array( |
|
373 |
+ 'tokens' => $tokens, |
|
374 |
+ 'not_deleted' => MSG_STATUS_NORMAL, |
|
375 |
+ 'operator' => !empty($context['search_params']['areas']['tickets']) ? '=' : '!=', |
|
376 |
+ ) |
|
377 |
+ ); |
|
378 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
379 |
+ $matches['messages'][$row['id_msg']][$row['id_word']] = true; |
|
380 |
+ $smcFunc['db_free_result']($query); |
|
381 |
+ |
|
382 |
+ if ($context['match_all']) |
|
383 |
+ foreach ($matches['messages'] as $ticket => $ticket_words) |
|
384 |
+ if (count($ticket_words) != $count_tokens) // How many words did we match in this subject? If it isn't the number we're expecting, ditch it. |
|
385 |
+ unset($matches['messages'][$ticket]); |
|
386 |
+ |
|
387 |
+ // Now, we just have a list of tickets to play with. Let's put that together in a master list. |
|
388 |
+ foreach ($matches['messages'] as $msg => $ticket_words) |
|
389 |
+ $matches['id_msg'][$ticket] = true; |
|
390 |
+ unset($matches['messages']); |
|
391 |
+ } |
|
392 |
+ |
|
393 |
+ // Aw, no matches? |
|
394 |
+ if (empty($matches['id_msg'])) |
|
395 |
+ return $context['sub_template'] = 'search_no_results'; |
|
396 |
+ |
|
397 |
+ $context['search_clauses'][] = 'hdtr.id_msg IN ({array_int:msg})'; |
|
398 |
+ $context['search_params']['msg'] = array_keys($matches['id_msg']); |
|
399 |
+ |
|
400 |
+ // How many results are there in total? |
|
401 |
+ $query = shd_db_query('', ' |
|
402 |
+ SELECT COUNT(*) |
|
403 |
+ FROM {db_prefix}helpdesk_tickets AS hdt |
|
404 |
+ INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdtr.id_ticket = hdt.id_ticket) |
|
405 |
+ WHERE ' . implode(' AND ', $context['search_clauses']) . ' LIMIT 1000', |
|
406 |
+ $context['search_params'] |
|
407 |
+ ); |
|
408 |
+ list($count) = $smcFunc['db_fetch_row']($query); |
|
409 |
+ if ($count == 0) |
|
410 |
+ { |
|
411 |
+ $smcFunc['db_free_result']($query); |
|
412 |
+ return $context['sub_template'] = 'search_no_results'; |
|
413 |
+ } |
|
414 |
+ // OK, at least one result, awesome. Are we off the end of the list? |
|
415 |
+ if ($context['search_params']['start'] > $count) |
|
416 |
+ { |
|
417 |
+ $context['search_params']['start'] = $count - ($count % $number_per_page); |
|
418 |
+ $context['pagenum'] = ($context['search_params']['start'] / $number_per_page) + 1; |
|
419 |
+ $context['num_results'] = $count); |
|
420 |
+ } |
|
421 |
+ |
|
422 |
+ // Get the results for displaying. |
|
423 |
+ $query = shd_db_query('', ' |
|
424 |
+ SELECT hdt.id_ticket, hdt.id_dept, hdd.dept_name, hdt.subject, hdt.urgency, hdt.private, hdt.last_updated, hdtr.body, |
|
425 |
+ hdtr.smileys_enabled, hdtr.id_member AS id_member, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time, |
|
426 |
+ hdt.id_first_msg, hdtr.id_msg |
|
427 |
+ FROM {db_prefix}helpdesk_ticket_replies AS hdtr |
|
428 |
+ INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdt.id_ticket = hdtr.id_ticket) |
|
429 |
+ INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept) |
|
430 |
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = hdtr.id_member) |
|
431 |
+ WHERE ' . implode(' AND ', $context['search_clauses']) . ' |
|
432 |
+ ORDER BY hdt.last_updated DESC, hdtr.id_msg DESC |
|
433 |
+ LIMIT {int:start}, {int:limit}', |
|
434 |
+ $context['search_params'] |
|
435 |
+ ); |
|
436 |
+ |
|
437 |
+ $context['search_results'] = array(); |
|
438 |
+ $page_pos = $context['search_params']['start']; // e.g. 0 on page 1, 10 for page 2, the first item will be page_pos + 1, so ++ it before using it. |
|
439 |
+ while ($row = $smcFunc['db_fetch_assoc']($query)) |
|
440 |
+ { |
|
441 |
+ $row['result'] = ++$page_pos; // Increment first, then use. |
|
442 |
+ $row['display_id'] = str_pad($row['id_ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT); |
|
443 |
+ $row['is_ticket'] = $row['id_msg'] == $row['id_first_msg']; // If the message we grabbed is the first message, this is actually a ticket, not a reply to one. |
|
444 |
+ $row['dept_link'] = !$context['shd_multi_dept'] ? '' : '[<a href="' . $scripturl . '?action=helpdesk;sa=main;dept=' . $row['id_dept'] . '">' . $row['dept_name'] . '</a>] '; |
|
445 |
+ |
|
446 |
+ $context['search_results'][] = $row; |
|
447 |
+ } |
|
448 |
+ |
|
449 |
+ return $context['sub_template'] = 'search_results'; |
|
450 |
+ } |
|
451 |
+} |
|
452 |
+ |
|
453 |
+function shd_get_named_people($field) |
|
454 |
+{ |
|
455 |
+ global $smcFunc, $sourcedir, $context; |
|
456 |
+ |
|
457 |
+ if (!isset($context['named_people'])) |
|
458 |
+ $context['named_people'] = array(); |
|
459 |
+ |
|
460 |
+ require_once($sourcedir . '/Subs-Auth.php'); |
|
461 |
+ |
|
462 |
+ $members = array(); |
|
463 |
+ // First look for the autosuggest values. |
|
464 |
+ if (!empty($_POST[$field . '_name_from']) && is_array($_POST[$field . '_name_from'])) |
|
465 |
+ foreach ($_POST['starter_name_from'] as $member) |
|
466 |
+ if ((int) $member > 0) |
|
467 |
+ $members[] = (int) $member; |
|
468 |
+ |
|
469 |
+ // Failing that, let's look at the name itself for those without JS. |
|
470 |
+ if (!empty($_POST[$field . '_name'])) |
|
471 |
+ { |
|
472 |
+ // We're going to take out the "s anyway ;). |
|
473 |
+ $names = strtr($_POST[$field . '_name'], array('\\"' => '"')); |
|
474 |
+ |
|
475 |
+ preg_match_all('~"([^"]+)"~', $names, $matches); |
|
476 |
+ $namedlist = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $names)))); |
|
477 |
+ |
|
478 |
+ foreach ($namedlist as $index => $name) |
|
479 |
+ if (strlen(trim($name)) > 0) |
|
480 |
+ $namedlist[$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($name))); |
|
481 |
+ else |
|
482 |
+ unset($namedlist[$index]); |
|
483 |
+ |
|
484 |
+ if (!empty($namedlist)) |
|
485 |
+ { |
|
486 |
+ $foundMembers = findMembers($namedlist); |
|
487 |
+ |
|
488 |
+ // Assume all are not found, until proven otherwise. |
|
489 |
+ $namesNotFound[$recipientType] = $namedlist; |
|
490 |
+ |
|
491 |
+ foreach ($foundMembers as $member) |
|
492 |
+ { |
|
493 |
+ $testNames = array( |
|
494 |
+ $smcFunc['strtolower']($member['username']), |
|
495 |
+ $smcFunc['strtolower']($member['name']), |
|
496 |
+ $smcFunc['strtolower']($member['email']), |
|
497 |
+ ); |
|
498 |
+ |
|
499 |
+ if (count(array_intersect($testNames, $namedRecipientList[$recipientType])) !== 0) |
|
500 |
+ { |
|
501 |
+ $members[] = $member['id']; |
|
502 |
+ |
|
503 |
+ $context['named_people'][$member['id']] = $member['real_name']; |
|
504 |
+ } |
|
505 |
+ } |
|
506 |
+ } |
|
507 |
+ } |
|
508 |
+ return array_unique($members); |
|
509 |
+} |
|
510 |
+?> |
|
0 | 511 |
\ No newline at end of file |
... | ... |
@@ -123,6 +123,8 @@ function shd_main() |
123 | 123 |
'restorereply' => array('SimpleDesk-Delete.php', 'shd_reply_restore'), |
124 | 124 |
'emaillog' => array('SimpleDesk-Notifications.php', 'shd_notify_popup'), |
125 | 125 |
'notify' => array('SimpleDesk-Notifications.php', 'shd_notify_ticket_options'), |
126 |
+ 'search' => array('SimpleDesk-Search.php', 'shd_search'), |
|
127 |
+ 'search2' => array('SimpleDesk-Search.php', 'shd_search2'), |
|
126 | 128 |
); |
127 | 129 |
|
128 | 130 |
// Navigation menu |
... | ... |
@@ -162,6 +164,12 @@ function shd_main() |
162 | 164 |
'lang' => true, |
163 | 165 |
'url' => $scripturl . '?action=helpdesk;sa=recyclebin' . $context['shd_dept_link'], |
164 | 166 |
), |
167 |
+ 'search' => array( |
|
168 |
+ 'text' => 'shd_search_menu', |
|
169 |
+ 'test' => 'can_shd_search', |
|
170 |
+ 'lang' => true, |
|
171 |
+ 'url' => $scripturl . '?action=helpdesk;sa=search', |
|
172 |
+ ), |
|
165 | 173 |
// Only for certain sub areas. |
166 | 174 |
'back' => array( |
167 | 175 |
'text' => 'shd_back_to_hd', |
... | ... |
@@ -306,6 +314,7 @@ function shd_main() |
306 | 314 |
$context['can_view_closed'] = shd_allowed_to(array('shd_view_closed_own', 'shd_view_closed_any'), $context['shd_department']); |
307 | 315 |
$context['can_view_recycle'] = shd_allowed_to('shd_access_recyclebin', $context['shd_department']); |
308 | 316 |
$context['display_back_to_hd'] = !in_array($_REQUEST['sa'], array('main', 'viewblock', 'recyclebin', 'closedtickets', 'dept')); |
317 |
+ $context['can_shd_search'] = shd_allowed_to('shd_search', 0); |
|
309 | 318 |
$context['can_view_options'] = shd_allowed_to(array('shd_view_preferences_own', 'shd_view_preferences_any'), 0); |
310 | 319 |
|
311 | 320 |
// Highlight the correct button. |
... | ... |
@@ -469,6 +469,8 @@ function shd_admin_bootstrap(&$admin_areas) |
469 | 469 |
'icon' => 'shd/maintenance.png', |
470 | 470 |
'function' => 'shd_admin_main', |
471 | 471 |
'subsections' => array( |
472 |
+ 'main' => array($txt['shd_admin_maint']), |
|
473 |
+ 'search' => array($txt['shd_maint_search_settings']), |
|
472 | 474 |
), |
473 | 475 |
), |
474 | 476 |
), |
... | ... |
@@ -60,8 +60,9 @@ function shd_load_all_permission_sets() |
60 | 60 |
'admin_helpdesk' => array(false, 'general', ''), // because they'll be managed from parent roles instead |
61 | 61 |
'shd_view_ticket' => array(true, 'general', 'ticket.png'), |
62 | 62 |
'shd_view_ticket_private' => array(true, 'general', 'ticket_private.png'), |
63 |
- 'shd_view_ip' => array(true, 'general', 'ip.png'), |
|
64 | 63 |
'shd_view_closed' => array(true, 'general', 'log_resolve.png'), |
64 |
+ 'shd_view_ip' => array(true, 'general', 'ip.png'), |
|
65 |
+ 'shd_search' => array(false, 'general', 'search.png'), |
|
65 | 66 |
|
66 | 67 |
'shd_new_ticket' => array(false, 'posting', 'log_newticket.png'), |
67 | 68 |
'shd_edit_ticket' => array(true, 'posting', 'log_editticket.png'), |
... | ... |
@@ -164,6 +165,7 @@ function shd_load_role_templates() |
164 | 165 |
'shd_view_ticket_private_any' => ROLEPERM_ALLOW, |
165 | 166 |
'shd_view_closed_any' => ROLEPERM_ALLOW, |
166 | 167 |
'shd_view_ip_own' => ROLEPERM_ALLOW, |
168 |
+ 'shd_search' => ROLEPERM_ALLOW, |
|
167 | 169 |
'shd_new_ticket' => ROLEPERM_ALLOW, |
168 | 170 |
'shd_edit_ticket_any' => ROLEPERM_ALLOW, |
169 | 171 |
'shd_reply_ticket_any' => ROLEPERM_ALLOW, |
... | ... |
@@ -208,6 +210,7 @@ function shd_load_role_templates() |
208 | 210 |
'shd_view_ticket_private_any' => ROLEPERM_ALLOW, |
209 | 211 |
'shd_view_closed_any' => ROLEPERM_ALLOW, |
210 | 212 |
'shd_view_ip_any' => ROLEPERM_ALLOW, |
213 |
+ 'shd_search' => ROLEPERM_ALLOW, |
|
211 | 214 |
'shd_new_ticket' => ROLEPERM_ALLOW, |
212 | 215 |
'shd_edit_ticket_any' => ROLEPERM_ALLOW, |
213 | 216 |
'shd_reply_ticket_any' => ROLEPERM_ALLOW, |
... | ... |
@@ -78,7 +78,7 @@ if (!defined('SMF')) |
78 | 78 |
*/ |
79 | 79 |
function shd_create_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions) |
80 | 80 |
{ |
81 |
- global $user_info, $txt, $modSettings, $smcFunc, $context, $user_profile; |
|
81 |
+ global $user_info, $txt, $modSettings, $smcFunc, $context, $user_profile, $sourcedir; |
|
82 | 82 |
|
83 | 83 |
// Clean them incoming vars up good 'n' proper |
84 | 84 |
$msgOptions['smileys_enabled'] = !empty($msgOptions['smileys_enabled']); |
... | ... |
@@ -365,6 +365,42 @@ function shd_create_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions) |
365 | 365 |
$smcFunc['db_free_result']($query); |
366 | 366 |
} |
367 | 367 |
|
368 |
+ if (empty($context['shd_no_search'])) |
|
369 |
+ { |
|
370 |
+ // Add words to the tables. |
|
371 |
+ require_once($sourcedir . '/sd_source/Subs-SimpleDeskSearch.php'); |
|
372 |
+ $words = shd_tokeniser($msgOptions['body']); |
|
373 |
+ if (!empty($words)) |
|
374 |
+ { |
|
375 |
+ $rows = array(); |
|
376 |
+ foreach ($words as $word) |
|
377 |
+ $rows[] = array($word, $msgOptions['id']); |
|
378 |
+ $smcFunc['db_insert']('replace', |
|
379 |
+ '{db_prefix}helpdesk_search_ticket_words', |
|
380 |
+ array('id_word' => 'string', 'id_msg' => 'int'), |
|
381 |
+ $rows, |
|
382 |
+ array('id_word', 'id_msg') |
|
383 |
+ ); |
|
384 |
+ } |
|
385 |
+ |
|
386 |
+ if ($new_ticket) |
|
387 |
+ { |
|
388 |
+ $words = shd_tokeniser($ticketOptions['subject']); |
|
389 |
+ if (!empty($words)) |
|
390 |
+ { |
|
391 |
+ $rows = array(); |
|
392 |
+ foreach ($words as $word) |
|
393 |
+ $rows[] = array($word, $ticketOptions['id']); |
|
394 |
+ $smcFunc['db_insert']('replace', |
|
395 |
+ '{db_prefix}helpdesk_search_subject_words', |
|
396 |
+ array('id_word' => 'string', 'id_ticket' => 'int'), |
|
397 |
+ $rows, |
|
398 |
+ array('id_word', 'id_ticket') |
|
399 |
+ ); |
|
400 |
+ } |
|
401 |
+ } |
|
402 |
+ } |
|
403 |
+ |
|
368 | 404 |
if (!empty($ticketOptions['dept'])) |
369 | 405 |
shd_clear_active_tickets($ticketOptions['dept']); |
370 | 406 |
|
... | ... |
@@ -416,7 +452,7 @@ function shd_create_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions) |
416 | 452 |
*/ |
417 | 453 |
function shd_modify_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions) |
418 | 454 |
{ |
419 |
- global $user_info, $txt, $modSettings, $smcFunc, $context; |
|
455 |
+ global $user_info, $txt, $modSettings, $smcFunc, $context, $sourcedir; |
|
420 | 456 |
|
421 | 457 |
$messages_columns = array(); |
422 | 458 |
$ticket_columns = array(); |
... | ... |
@@ -653,6 +689,60 @@ function shd_modify_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions) |
653 | 689 |
); |
654 | 690 |
} |
655 | 691 |
|
692 |
+ if (empty($context['shd_no_search']) && !empty($msgOptions['id'])) |
|
693 |
+ { |
|
694 |
+ // Clear the original entries. |
|
695 |
+ $smcFunc['db_query']('', ' |
|
696 |
+ DELETE FROM {db_prefix}helpdesk_search_ticket_words |
|
697 |
+ WHERE id_msg = {int:msg}', |
|
698 |
+ array( |
|
699 |
+ 'msg' => $msgOptions['id'], |
|
700 |
+ ) |
|
701 |
+ ); |
|
702 |
+ // Add words to the tables. |
|
703 |
+ require_once($sourcedir . '/sd_source/Subs-SimpleDeskSearch.php'); |
|
704 |
+ if (!empty($msgOptions['body'])) |
|
705 |
+ { |
|
706 |
+ $words = shd_tokeniser($msgOptions['body']); |
|
707 |
+ if (!empty($words)) |
|
708 |
+ { |
|
709 |
+ $rows = array(); |
|
710 |
+ foreach ($words as $word) |
|
711 |
+ $rows[] = array($word, $msgOptions['id']); |
|
712 |
+ $smcFunc['db_insert']('replace', |
|
713 |
+ '{db_prefix}helpdesk_search_ticket_words', |
|
714 |
+ array('id_word' => 'string', 'id_msg' => 'int'), |
|
715 |
+ $rows, |
|
716 |
+ array('id_word', 'id_msg') |
|
717 |
+ ); |
|
718 |
+ } |
|
719 |
+ } |
|
720 |
+ |
|
721 |
+ if (isset($ticketOptions['subject'])) |
|
722 |
+ { |
|
723 |
+ $smcFunc['db_query']('', ' |
|
724 |
+ DELETE FROM {db_prefix}helpdesk_search_subject_words |
|
725 |
+ WHERE id_ticket = {int:ticket}', |
|
726 |
+ array( |
|
727 |
+ 'ticket' => $ticketOptions['id'], |
|
728 |
+ ) |
|
729 |
+ ); |
|
730 |
+ $words = shd_tokeniser($ticketOptions['subject']); |
|
731 |
+ if (!empty($words)) |
|
732 |
+ { |
|
733 |
+ $rows = array(); |
|
734 |
+ foreach ($words as $word) |
|
735 |
+ $rows[] = array($word, $ticketOptions['id']); |
|
736 |
+ $smcFunc['db_insert']('replace', |
|
737 |
+ '{db_prefix}helpdesk_search_subject_words', |
|
738 |
+ array('id_word' => 'string', 'id_ticket' => 'int'), |
|
739 |
+ $rows, |
|
740 |
+ array('id_word', 'id_ticket') |
|
741 |
+ ); |
|
742 |
+ } |
|
743 |
+ } |
|
744 |
+ } |
|
745 |
+ |
|
656 | 746 |
// Int hook |
657 | 747 |
call_integration_hook('shd_hook_modpost', array(&$msgOptions, &$ticketOptions, &$posterOptions)); |
658 | 748 |
|
... | ... |
@@ -0,0 +1,140 @@ |
1 |
+<?php |
|
2 |
+############################################################### |
|
3 |
+# Simple Desk Project - www.simpledesk.net # |
|
4 |
+############################################################### |
|
5 |
+# An advanced help desk modifcation built on SMF # |
|
6 |
+############################################################### |
|
7 |
+# # |
|
8 |
+# * Copyright 2010 - SimpleDesk.net # |
|
9 |
+# # |
|
10 |
+# This file and its contents are subject to the license # |
|
11 |
+# included with this distribution, license.txt, which # |
|
12 |
+# states that this software is New BSD Licensed. # |
|
13 |
+# Any questions, please contact SimpleDesk.net # |
|
14 |
+# # |
|
15 |
+############################################################### |
|
16 |
+# SimpleDesk Version: 2.0 Anatidae # |
|
17 |
+# File Info: Subs-SimpleDeskSearch.php / 2.0 Anatidae # |
|
18 |
+############################################################### |
|
19 |
+ |
|
20 |
+/** |
|
21 |
+ * This file handles the backbone of searches, such as the tokeniser and manages getting the tables actually maintained. |
|
22 |
+ * |
|
23 |
+ * @package source |
|
24 |
+ * @since 2.0 |
|
25 |
+*/ |
|
26 |
+ |
|
27 |
+if (!defined('SMF')) |
|
28 |
+ die('Hacking attempt...'); |
|
29 |
+ |
|
30 |
+/** |
|
31 |
+ * Identify and return the character set parameters for searching. |
|
32 |
+ * |
|
33 |
+ * @return An array of two items, the overall character set currently in use and the list of characters to be permitted in searches in the form of a regular expression character class. |
|
34 |
+ * @see shd_return_exclude_regex() |
|
35 |
+*/ |
|
36 |
+function shd_search_charset() |
|
37 |
+{ |
|
38 |
+ global $context, $modSettings, $txt; |
|
39 |
+ |
|
40 |
+ $utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1); |
|
41 |
+ $charset = !empty($txt['lang_character_set']) ? $txt['lang_character_set'] : ($utf8 ? 'UTF-8' : 'ISO-8859-1'); |
|
42 |
+ |
|
43 |
+ if (empty($modSettings['shd_search_charset'])) |
|
44 |
+ $modSettings['shd_search_charset'] = '0..9, A..Z, a..z, &, ~'; |
|
45 |
+ |
|
46 |
+ $modSettings['shd_search_min_size'] = !empty($modSettings['shd_search_min_size']) ? $modSettings['shd_search_min_size'] : 3; |
|
47 |
+ $modSettings['shd_search_max_size'] = !empty($modSettings['shd_search_max_size']) ? $modSettings['shd_search_max_size'] : 8; |
|
48 |
+ $modSettings['shd_search_prefix_size'] = !empty($modSettings['shd_search_prefix_size']) ? $modSettings['shd_search_prefix_size'] : 0; |
|
49 |
+ |
|
50 |
+ $terms = explode(',', $modSettings['shd_search_charset']); |
|
51 |
+ $exclude_regex = ''; |
|
52 |
+ foreach ($terms as $k => $v) |
|
53 |
+ { |
|
54 |
+ $v = trim($v); |
|
55 |
+ if (preg_match('~^(.)$~i' . ($context['utf8'] ? 'u' : ''), $v, $match)) // Single character |
|
56 |
+ $exclude_regex .= preg_quote($match[1], '~'); |
|
57 |
+ elseif (preg_match('~^(.)\.\.(.)$~i' . ($context['utf8'] ? 'u' : ''), $v, $match)) // It's a ranged component. |
|
58 |
+ $exclude_regex .= preg_quote($match[1], '~') . '-' . preg_quote($match[2], '~'); |
|
59 |
+ } |
|
60 |
+ if (empty($exclude_regex)) |
|
61 |
+ $exclude_regex = ''; |
|
62 |
+ else |
|
63 |
+ $exclude_regex = '~[^' . $exclude_regex . ']+~' . ($context['utf8'] ? 'u' : ''); |
|
64 |
+ |
|
65 |
+ return array($charset, $exclude_regex); |
|
66 |
+} |
|
67 |
+ |
|
68 |
+/** |
|
69 |
+ * Takes an input string and returns a large array of word and word position identifiers. |
|
70 |
+ * |
|
71 |
+ * @param string $string A regular post's contents, or that of the subject of a post. |
|
72 |
+ * @return array An array containing the word identifiers. |
|
73 |
+*/ |
|
74 |
+function shd_tokeniser($string) |
|
75 |
+{ |
|
76 |
+ global $smcFunc, $modSettings; |
|
77 |
+ static $charset = null, $exclude_regex = ''; |
|
78 |
+ |
|
79 |
+ $result = array(); |
|
80 |
+ |
|
81 |
+ if ($charset === null) |
|
82 |
+ list($charset, $exclude_regex) = shd_search_charset(); |
|
83 |
+ |
|
84 |
+ // Step 1. Convert entities back to characters, regardless of what they are. |
|
85 |
+ $string = html_entity_decode($string, ENT_QUOTES, $charset); |
|
86 |
+ |
|
87 |
+ // Step 2. Strip wiki code then bbcode. |
|
88 |
+ $string = preg_replace('~\[\[[^\]]+\]\]~U', '', $string); |
|
89 |
+ $string = preg_replace('~\[[^\]]+\]~U', '', $string); |
|
90 |
+ |
|
91 |
+ // Step 3. Strip certain minimal HTML. |
|
92 |
+ $string = preg_replace('~</?(img|br|hr|b|i|u|strike|s|ins|del|ol|ul|li|p|div|span|table|tr|th|td|code|pre)[^>]+>~iU', ' ', $string); |
|
93 |
+ |
|
94 |
+ // Step 3. Strip characters we're not interested in. |
|
95 |
+ if ($exclude_regex === '') // If we have no character types, we can't match anything. |
|
96 |
+ return array(); |
|
97 |
+ |
|
98 |
+ $string = preg_replace($exclude_regex, ' ', $string); |
|
99 |
+ $string = trim(preg_replace('~\s+~', ' ', $string)); |
|
100 |
+ |
|
101 |
+ // Step 4. Break into an array and start tokenising. |
|
102 |
+ $array = explode(' ', $string); |
|
103 |
+ |
|
104 |
+ $i = 0; |
|
105 |
+ foreach ($array as $position => $word) |
|
106 |
+ { |
|
107 |
+ $len = $smcFunc['strlen']($word); |
|
108 |
+ if ($len >= $modSettings['shd_search_min_size'] && $len <= $modSettings['shd_search_max_size']) |
|
109 |
+ { |
|
110 |
+ $word = $smcFunc['strtolower']($word); |
|
111 |
+ $result[shd_hash($word)] = $i++; |
|
112 |
+ if (!empty($modSettings['shd_search_prefix_size']) && $len >= $modSettings['shd_search_prefix_size']) |
|
113 |
+ { |
|
114 |
+ for ($j = $modSettings['shd_search_prefix_size']; $j <= $len; $j++) |
|
115 |
+ { |
|
116 |
+ $prefixword = substr($word, 0, $j) . chr(7); |
|
117 |
+ $result[shd_hash($prefixword)] = $i++; |
|
118 |
+ } |
|
119 |
+ } |
|
120 |
+ } |
|
121 |
+ } |
|
122 |
+ |
|
123 |
+ return array_flip($result); // This gets us a unique array but done faster than $result[] = shd_hash($word); $result = array_unique($result); |
|
124 |
+} |
|
125 |
+ |
|
126 |
+/** |
|
127 |
+ * Creates our hash. Due to the way floats can be used, we can safely store an integer equal to 2^52 in a float, so we'll use this. It should be relatively free from avalanching. |
|
128 |
+ * |
|
129 |
+ * Theoretically, a 32 bit hash (a la CRC32) would be suitable if it didn't have the collision incidence factor it does, so we have to do it this way. |
|
130 |
+ * If we didn't permit prefix matching it would probably be suitable, actually. |
|
131 |
+ * |
|
132 |
+ * @param string $string The string to take the hash of. |
|
133 |
+ * @return string $hash The 52 bit number as a string to prevent it being mashed by any more formatting. |
|
134 |
+*/ |
|
135 |
+function shd_hash($string) |
|
136 |
+{ |
|
137 |
+ return sprintf('%0.0f', hexdec(substr(sha1($string), -13))); |
|
138 |
+} |
|
139 |
+ |
|
140 |
+?> |
|
0 | 141 |
\ No newline at end of file |
... | ... |
@@ -17,20 +17,6 @@ function template_shd_admin_maint_home() |
17 | 17 |
{ |
18 | 18 |
global $context, $settings, $txt, $modSettings, $scripturl; |
19 | 19 |
|
20 |
- echo ' |
|
21 |
- <div id="admincenter"> |
|
22 |
- <div class="tborder"> |
|
23 |
- <div class="cat_bar"> |
|
24 |
- <h3 class="catbg"> |
|
25 |
- <img src="', $settings['default_images_url'], '/simpledesk/maintenance.png" class="icon" alt="*" /> |
|
26 |
- ', $txt['shd_admin_maint'], ' |
|
27 |
- </h3> |
|
28 |
- </div> |
|
29 |
- <p class="description"> |
|
30 |
- ', $txt['shd_admin_maint_desc'], ' |
|
31 |
- </p> |
|
32 |
- </div>'; |
|
33 |
- |
|
34 | 20 |
// OK, recount all the important figures. |
35 | 21 |
echo ' |
36 | 22 |
<div class="cat_bar grid_header"> |
... | ... |
@@ -189,30 +175,12 @@ function template_shd_admin_maint_home() |
189 | 175 |
</div> |
190 | 176 |
<span class="lowerframe"><span></span></span><br />'; |
191 | 177 |
} |
192 |
- |
|
193 |
- // And we're done. |
|
194 |
- echo ' |
|
195 |
- </div>'; |
|
196 | 178 |
} |
197 | 179 |
|
198 | 180 |
function template_shd_admin_maint_findrepairdone() |
199 | 181 |
{ |
200 | 182 |
global $context, $settings, $txt, $scripturl; |
201 | 183 |
|
202 |
- echo ' |
|
203 |
- <div id="admincenter"> |
|
204 |
- <div class="tborder"> |
|
205 |
- <div class="cat_bar"> |
|
206 |
- <h3 class="catbg"> |
|
207 |
- <img src="', $settings['default_images_url'], '/simpledesk/find_repair.png" class="icon" alt="*" /> |
|
208 |
- ', $txt['shd_admin_maint_findrepair'], ' |
|
209 |
- </h3> |
|
210 |
- </div> |
|
211 |
- <p class="description"> |
|
212 |
- ', $txt['shd_admin_maint_findrepair_desc'], ' |
|
213 |
- </p> |
|
214 |
- </div>'; |
|
215 |
- |
|
216 | 184 |
if (empty($context['maintenance_result'])) |
217 | 185 |
{ |
218 | 186 |
// Yay everything was fine. |
... | ... |
@@ -268,10 +236,6 @@ function template_shd_admin_maint_findrepairdone() |
268 | 236 |
<span class="botslice"><span></span></span> |
269 | 237 |
</div>'; |
270 | 238 |
} |
271 |
- |
|
272 |
- // And we're done. |
|
273 |
- echo ' |
|
274 |
- </div>'; |
|
275 | 239 |
} |
276 | 240 |
|
277 | 241 |
function template_shd_admin_maint_reattributedone() |
... | ... |
@@ -279,18 +243,6 @@ function template_shd_admin_maint_reattributedone() |
279 | 243 |
global $context, $settings, $txt, $scripturl; |
280 | 244 |
|
281 | 245 |
echo ' |
282 |
- <div id="admincenter"> |
|
283 |
- <div class="tborder"> |
|
284 |
- <div class="cat_bar"> |
|
285 |
- <h3 class="catbg"> |
|
286 |
- <img src="', $settings['default_images_url'], '/simpledesk/user.png" class="icon" alt="*" /> |
|
287 |
- ', $txt['shd_admin_maint_reattribute'], ' |
|
288 |
- </h3> |
|
289 |
- </div> |
|
290 |
- <p class="description"> |
|
291 |
- ', $txt['shd_admin_maint_reattribute_desc'], ' |
|
292 |
- </p> |
|
293 |
- </div> |
|
294 | 246 |
<div class="windowbg"> |
295 | 247 |
<span class="topslice"><span></span></span> |
296 | 248 |
<div class="content"> |
... | ... |
@@ -300,7 +252,6 @@ function template_shd_admin_maint_reattributedone() |
300 | 252 |
</p> |
301 | 253 |
</div> |
302 | 254 |
<span class="botslice"><span></span></span> |
303 |
- </div> |
|
304 | 255 |
</div>'; |
305 | 256 |
} |
306 | 257 |
|
... | ... |
@@ -310,17 +261,6 @@ function template_shd_admin_maint_massdeptmovedone() |
310 | 261 |
|
311 | 262 |
echo ' |
312 | 263 |
<div id="admincenter"> |
313 |
- <div class="tborder"> |
|
314 |
- <div class="cat_bar"> |
|
315 |
- <h3 class="catbg"> |
|
316 |
- <img src="', $settings['default_images_url'], '/simpledesk/movedept.png" class="icon" alt="*" /> |
|
317 |
- ', $txt['shd_admin_maint_massdeptmove'], ' |
|
318 |
- </h3> |
|
319 |
- </div> |
|
320 |
- <p class="description"> |
|
321 |
- ', $txt['shd_admin_maint_massdeptmove_desc'], ' |
|
322 |
- </p> |
|
323 |
- </div> |
|
324 | 264 |
<div class="windowbg"> |
325 | 265 |
<span class="topslice"><span></span></span> |
326 | 266 |
<div class="content"> |
... | ... |
@@ -334,4 +274,77 @@ function template_shd_admin_maint_massdeptmovedone() |
334 | 274 |
</div>'; |
335 | 275 |
} |
336 | 276 |
|
277 |
+function template_shd_admin_maint_search() |
|
278 |
+{ |
|
279 |
+ global $context, $settings, $txt, $scripturl, $modSettings; |
|
280 |
+ |
|
281 |
+ if (isset($_GET['rebuilddone'])) |
|
282 |
+ echo ' |
|
283 |
+ <div class="maintenance_finished"> |
|
284 |
+ ', $txt['shd_search_rebuilt'], ' |
|
285 |
+ </div>'; |
|
286 |
+ |
|
287 |
+ echo ' |
|
288 |
+ <div class="cat_bar grid_header"> |
|
289 |
+ <h3 class="catbg"> |
|
290 |
+ <img src="', $settings['default_images_url'], '/simpledesk/search.png" alt="*"> |
|
291 |
+ ', $txt['shd_maint_rebuild_index'], ' |
|
292 |
+ </h3> |
|
293 |
+ </div> |
|
294 |
+ <div class="roundframe"> |
|
295 |
+ <div class="content"> |
|
296 |
+ <p>', $txt['shd_maint_rebuild_index_desc'], '</p> |
|
297 |
+ <form action="', $scripturl, '?action=admin;area=helpdesk_maint;sa=search" method="post"> |
|
298 |
+ <input type="submit" name="rebuild" value="', $txt['maintain_run_now'], '" onclick="return submitThisOnce(this);" class="button_submit"> |
|
299 |
+ <input type="hidden" name="start" value="0" /> |
|
300 |
+ <input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '"> |
|
301 |
+ </form> |
|
302 |
+ </div> |
|
303 |
+ </div> |
|
304 |
+ <span class="lowerframe"><span></span></span><br /> |
|
305 |
+ <div class="cat_bar grid_header"> |
|
306 |
+ <h3 class="catbg"> |
|
307 |
+ <img src="', $settings['default_images_url'], '/simpledesk/search.png" alt="*"> |
|
308 |
+ ', $txt['shd_maint_search_settings'], ' |
|
309 |
+ </h3> |
|
310 |
+ </div> |
|
311 |
+ <div class="roundframe"> |
|
312 |
+ <div class="content"> |
|
313 |
+ <form action="', $scripturl, '?action=admin;area=helpdesk_maint;sa=search" method="post"> |
|
314 |
+ <div class="errorbox"><img src="', $settings['default_images_url'], '/simpledesk/warning.png" alt="*" class="shd_icon_minihead" /> ', $txt['shd_maint_search_settings_warning'], '</div> |
|
315 |
+ <dl class="settings"> |
|
316 |
+ <dt> |
|
317 |
+ ', $txt['shd_search_min_size'], ' |
|
318 |
+ </dt> |
|
319 |
+ <dd> |
|
320 |
+ <input type="text" class="input_text" name="shd_search_min_size" size="4" value="', $modSettings['shd_search_min_size'], '" /> |
|
321 |
+ </dd> |
|
322 |
+ <dt> |
|
323 |
+ ', $txt['shd_search_max_size'], ' |
|
324 |
+ </dt> |
|
325 |
+ <dd> |
|
326 |
+ <input type="text" class="input_text" name="shd_search_max_size" size="4" value="', $modSettings['shd_search_max_size'], '" /> |
|
327 |
+ </dd> |
|
328 |
+ <dt> |
|
329 |
+ <a id="setting_shd_search_prefix_size" href="', $scripturl, '?action=helpadmin;help=shd_search_prefix_size_help" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.gif" class="icon" alt="?"></a> |
|
330 |
+ <span>', $txt['shd_search_prefix_size'], '</span> |
|
331 |
+ </dt> |
|
332 |
+ <dd> |
|
333 |
+ <input type="text" class="input_text" name="shd_search_prefix_size" size="4" value="', $modSettings['shd_search_prefix_size'], '" /> |
|
334 |
+ </dd> |
|
335 |
+ <dt> |
|
336 |
+ ', $txt['shd_search_charset'], ' |
|
337 |
+ </dt> |
|
338 |
+ <dd> |
|
339 |
+ <textarea name="shd_search_charset" rows="3" cols="35" style="width: 99%;">', htmlspecialchars($modSettings['shd_search_charset']), '</textarea> |
|
340 |
+ </dd> |
|
341 |
+ </dl> |
|
342 |
+ <span><input type="submit" name="save" value="', $txt['save'], '" class="button_submit" /></span> |
|
343 |
+ <input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '"> |
|
344 |
+ </form> |
|
345 |
+ </div> |
|
346 |
+ </div> |
|
347 |
+ <span class="lowerframe"><span></span></span><br />'; |
|
348 |
+} |
|
349 |
+ |
|
337 | 350 |
?> |
338 | 351 |
\ No newline at end of file |
... | ... |
@@ -0,0 +1,357 @@ |
1 |
+<?php |
|
2 |
+// Version: 2.0 Anatidae; SimpleDesk searches. |
|
3 |
+ |
|
4 |
+/** |
|
5 |
+ * Handles searching tickets. |
|
6 |
+ * |
|
7 |
+ * @package template |
|
8 |
+ * @since 1.0 |
|
9 |
+*/ |
|
10 |
+ |
|
11 |
+/** |
|
12 |
+ * Displays the list of possible searching criteria. |
|
13 |
+ * |
|
14 |
+ * @see shd_search() |
|
15 |
+ * @since 2.0 |
|
16 |
+*/ |
|
17 |
+function template_search() |
|
18 |
+{ |
|
19 |
+ global $context, $txt, $scripturl, $settings, $modSettings; |
|
20 |
+ |
|
21 |
+ // Back to the helpdesk. |
|
22 |
+ echo ' |
|
23 |
+ <div class="floatleft"> |
|
24 |
+ ', template_button_strip(array($context['navigation']['back']), 'bottom'), ' |
|
25 |
+ </div><br class="clear" /><br />'; |
|
26 |
+ |
|
27 |
+ if (!empty($modSettings['shd_new_search_index'])) |
|
28 |
+ echo ' |
|
29 |
+ <div class="errorbox"><img src="', $settings['default_images_url'], '/simpledesk/warning.png" alt="*" class="shd_icon_minihead" /> ', shd_allowed_to('admin_helpdesk', 0) ? $txt['shd_search_warning_admin'] : $txt['shd_search_warning_nonadmin'], '</div>'; |
|
30 |
+ |
|
31 |
+ echo ' |
|
32 |
+ <div class="cat_bar grid_header"> |
|
33 |
+ <h3 class="catbg"> |
|
34 |
+ <img src="', $settings['default_images_url'], '/simpledesk/search.png" alt="*" /> |
|
35 |
+ ', $txt['shd_search'], ' |
|
36 |
+ </h3> |
|
37 |
+ </div> |
|
38 |
+ <div class="roundframe"> |
|
39 |
+ <form action="', $scripturl, '?action=helpdesk;sa=search2" method="post"> |
|
40 |
+ <div class="content"> |
|
41 |
+ <br /> |
|
42 |
+ <dl class="settings"> |
|
43 |
+ <dt> |
|
44 |
+ <strong>', $txt['shd_search_text'], '</strong> |
|
45 |
+ </dt> |
|
46 |
+ <dd> |
|
47 |
+ <input type="text" name="search" value="" size="40" maxlength="100" class="input_text" /> |
|
48 |
+ </dd> |
|
49 |
+ <dt> |
|
50 |
+ <strong>', $txt['shd_search_match'], '</strong> |
|
51 |
+ </dt> |
|
52 |
+ <dd> |
|
53 |
+ <select name="searchtype"> |
|
54 |
+ <option value="all">', $txt['shd_search_match_all'], '</option> |
|
55 |
+ <option value="any">', $txt['shd_search_match_any'], '</option> |
|
56 |
+ </select> |
|
57 |
+ </dd> |
|
58 |
+ </dl> |
|
59 |
+ <br /> |
|
60 |
+ <dl class="settings"> |
|
61 |
+ <dt> |
|
62 |
+ <strong>', $txt['shd_search_where'], '</strong> |
|
63 |
+ </dt> |
|
64 |
+ <dd> |
|
65 |
+ <input type="checkbox" class="input_check" checked="checked" name="search_subjects" /> ', $txt['shd_search_where_subjects'], '<br /> |
|
66 |
+ <input type="checkbox" class="input_check" checked="checked" name="search_tickets" /> ', $txt['shd_search_where_tickets'], '<br /> |
|
67 |
+ <input type="checkbox" class="input_check" checked="checked" name="search_replies" /> ', $txt['shd_search_where_replies'], '<br /> |
|
68 |
+ </dd> |
|
69 |
+ </dl>'; |
|
70 |
+ |
|
71 |
+ if (count($context['dept_list']) == 1) |
|
72 |
+ { |
|
73 |
+ $array = array_keys($context['dept_list']); |
|
74 |
+ echo ' |
|
75 |
+ <input type="hidden" name="search_dept[]" value="', $array[0], '" />'; |
|
76 |
+ } |
|
77 |
+ else |
|
78 |
+ { |
|
79 |
+ echo ' |
|
80 |
+ <hr /> |
|
81 |
+ <dl class="settings"> |
|
82 |
+ <dt> |
|
83 |
+ <strong>', $txt['shd_search_dept'], '</strong> |
|
84 |
+ </dt> |
|
85 |
+ <dd>'; |
|
86 |
+ |
|
87 |
+ foreach ($context['dept_list'] as $id => $name) |
|
88 |
+ echo ' |
|
89 |
+ <input type="checkbox" class="input_check" checked="checked" name="search_dept[]" value="', $id, '" /> ', $name, '<br />'; |
|
90 |
+ |
|
91 |
+ echo ' |
|
92 |
+ </dd> |
|
93 |
+ </dl>'; |
|
94 |
+ } |
|
95 |
+ |
|
96 |
+ echo ' |
|
97 |
+ <hr /> |
|
98 |
+ <dl class="settings"> |
|
99 |
+ <dt> |
|
100 |
+ <strong>', $txt['shd_search_scope'], '</strong> |
|
101 |
+ </dt> |
|
102 |
+ <dd> |
|
103 |
+ <input type="checkbox" class="input_check" checked="checked" name="scope_open" /> ', $txt['shd_search_scope_open'], '<br /> |
|
104 |
+ <input type="checkbox" class="input_check" checked="checked" name="scope_closed" /> ', $txt['shd_search_scope_closed'], '<br /> |
|
105 |
+ <input type="checkbox" class="input_check" checked="checked" name="scope_recycle" /> ', $txt['shd_search_scope_recycle'], '<br /> |
|
106 |
+ </dd> |
|
107 |
+ </dl> |
|
108 |
+ <hr /> |
|
109 |
+ <dl class="settings"> |
|
110 |
+ <dt> |
|
111 |
+ <strong>', $txt['shd_search_urgency'], '</strong> |
|
112 |
+ </dt> |
|
113 |
+ <dd>'; |
|
114 |
+ |
|
115 |
+ // All the urgency levels, currently 0-5. |
|
116 |
+ for ($i = 0; $i <= 5; $i++) |
|
117 |
+ echo ' |
|
118 |
+ <input type="checkbox" class="input_check" checked="checked" name="urgency[]" value="', $i, '" /> ', $txt['shd_urgency_' . $i], '<br />'; |
|
119 |
+ |
|
120 |
+ echo ' |
|
121 |
+ </dd> |
|
122 |
+ </dl> |
|
123 |
+ <hr /> |
|
124 |
+ <dl class="settings"> |
|
125 |
+ <dt> |
|
126 |
+ <strong>', $txt['shd_search_ticket_starter'], '</strong> |
|
127 |
+ <div class="smalltext">', $txt['shd_search_ticket_named_person'], '</div> |
|
128 |
+ </dt> |
|
129 |
+ <dd> |
|
130 |
+ <input type="hidden" name="starter" value="" /> |
|
131 |
+ <input type="text" name="starter_name" id="starter_name" size="40" maxlength="100" class="input_text" value="" /> |
|
132 |
+ <div id="starter_name_container"></div> |
|
133 |
+ </dd> |
|
134 |
+ </dl> |
|
135 |
+ <br /> |
|
136 |
+ <dl class="settings"> |
|
137 |
+ <dt> |
|
138 |
+ <strong>', $txt['shd_search_ticket_assignee'], '</strong> |
|
139 |
+ <div class="smalltext">', $txt['shd_search_ticket_named_person'], '</div> |
|
140 |
+ </dt> |
|
141 |
+ <dd> |
|
142 |
+ <input type="hidden" name="assignee" value="" /> |
|
143 |
+ <input type="text" name="assignee_name" id="assignee_name" size="40" maxlength="100" class="input_text" value="" /> |
|
144 |
+ <div id="assignee_name_container"></div> |
|
145 |
+ </dd> |
|
146 |
+ </dl> |
|
147 |
+ |
|
148 |
+ <script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/suggest.js?20fin"></script> |
|
149 |
+ <script type="text/javascript"><!-- // --><![CDATA[ |
|
150 |
+ var oTicketStarter = new smc_AutoSuggest({ |
|
151 |
+ sSelf: \'oTicketStarter\', |
|
152 |
+ sSessionId: \'', $context['session_id'], '\', |
|
153 |
+ sSessionVar: \'', $context['session_var'], '\', |
|
154 |
+ sControlId: \'starter_name\', |
|
155 |
+ sSuggestId: \'starter\', |
|
156 |
+ sSearchType: \'member\', |
|
157 |
+ sPostName: \'starter_name_form\', |
|
158 |
+ sURLMask: \'action=profile;u=%item_id%\', |
|
159 |
+ bItemList: true, |
|
160 |
+ sItemListContainerId: \'starter_name_container\', |
|
161 |
+ aListItems: [] |
|
162 |
+ }); |
|
163 |
+ var oTicketAssignee = new smc_AutoSuggest({ |
|
164 |
+ sSelf: \'oTicketAssignee\', |
|
165 |
+ sSessionId: \'', $context['session_id'], '\', |
|
166 |
+ sSessionVar: \'', $context['session_var'], '\', |
|
167 |
+ sControlId: \'assignee_name\', |
|
168 |
+ sSuggestId: \'assignee\', |
|
169 |
+ sSearchType: \'member\', |
|
170 |
+ sPostName: \'assigned_name_form\', |
|
171 |
+ sURLMask: \'action=profile;u=%item_id%\', |
|
172 |
+ bItemList: true, |
|
173 |
+ sItemListContainerId: \'assignee_name_container\', |
|
174 |
+ aListItems: [] |
|
175 |
+ }); |
|
176 |
+ // ]', ']></script> |
|
177 |
+ <hr /> |
|
178 |
+ <br /> |
|
179 |
+ <input type="submit" value="', $txt['shd_search'], '" onclick="return submitThisOnce(this);" accesskey="s" class="button_submit" /> |
|
180 |
+ </div> |
|
181 |
+ </form> |
|
182 |
+ </div> |
|
183 |
+ <span class="lowerframe"><span></span></span>'; |
|
184 |
+} |
|
185 |
+ |
|
186 |
+function template_search_no_results() |
|
187 |
+{ |
|
188 |
+ global $context, $txt, $scripturl, $settings, $modSettings; |
|
189 |
+ |
|
190 |
+ // Back to the helpdesk. |
|
191 |
+ echo ' |
|
192 |
+ <div class="floatleft"> |
|
193 |
+ ', template_button_strip(array($context['navigation']['back'], $context['navigation']['search']), 'bottom'), ' |
|
194 |
+ </div><br class="clear" /><br />'; |
|
195 |
+ |
|
196 |
+ echo ' |
|
197 |
+ <div class="cat_bar grid_header"> |
|
198 |
+ <h3 class="catbg"> |
|
199 |
+ <img src="', $settings['default_images_url'], '/simpledesk/search.png" alt="*" /> |
|
200 |
+ ', $txt['shd_search'], ' |
|
201 |
+ </h3> |
|
202 |
+ </div>'; |
|
203 |
+ |
|
204 |
+ // Search criteria |
|
205 |
+ template_search_criteria(); |
|
206 |
+ |
|
207 |
+ echo ' |
|
208 |
+ <span class="upperframe"><span></span></span> |
|
209 |
+ <div class="roundframe"> |
|
210 |
+ <div class="content">', $txt['shd_search_no_results'], '</div> |
|
211 |
+ </div> |
|
212 |
+ <span class="lowerframe"><span></span></span>'; |
|
213 |
+} |
|
214 |
+ |
|
215 |
+function template_search_criteria() |
|
216 |
+{ |
|
217 |
+ global $context, $txt, $scripturl, $settings, $modSettings, $smcFunc; |
|
218 |
+ |
|
219 |
+ if (!empty($context['search_params'])) |
|
220 |
+ { |
|
221 |
+ echo ' |
|
222 |
+ <div class="information"> |
|
223 |
+ <strong>', $txt['shd_search_criteria'], '</strong> |
|
224 |
+ <ul>'; |
|
225 |
+ |
|
226 |
+ // We go through the form step by step. |
|
227 |
+ if (!empty($context['search_terms'])) |
|
228 |
+ { |
|
229 |
+ echo ' |
|
230 |
+ <li>', $txt['shd_search_text'], ' ', $smcFunc['htmlspecialchars']($context['search_terms']), ' (', $context['match_all'] ? $txt['shd_search_match_all'] : $txt['shd_search_match_any'], ')</li>'; |
|
231 |
+ |
|
232 |
+ // Since we're here, we also need to attend to which items we searched. |
|
233 |
+ $items = array(); |
|
234 |
+ foreach ($context['search_params']['areas'] as $k => $v) |
|
235 |
+ $items[] = $txt['shd_search_where_' . $k]; |
|
236 |
+ |
|
237 |
+ echo ' |
|
238 |
+ <li>', $txt['shd_search_where'], ' ', implode(', ', $items), '</li>'; |
|
239 |
+ } |
|
240 |
+ |
|
241 |
+ // Departments. Don't bother if the user can only see one department. |
|
242 |
+ if (!empty($context['search_dept_list'])) |
|
243 |
+ echo ' |
|
244 |
+ <li>', $txt['shd_search_dept'], ' ', implode(', ', $context['search_dept_list']), '</li>'; |
|
245 |
+ |
|
246 |
+ // What type of tickets? |
|
247 |
+ if (!empty($context['search_params']['status'])) |
|
248 |
+ { |
|
249 |
+ $status = array(); |
|
250 |
+ if (!empty($_POST['scope_open'])) |
|
251 |
+ $status[] = $txt['shd_search_scope_open']; |
|
252 |
+ if (!empty($_POST['scope_closed'])) |
|
253 |
+ $status[] = $txt['shd_search_scope_closed']; |
|
254 |
+ if (!empty($_POST['scope_recycle'])) |
|
255 |
+ $status[] = $txt['shd_search_scope_recycle']; |
|
256 |
+ |
|
257 |
+ echo ' |
|
258 |
+ <li>', $txt['shd_search_scope'], ' ', implode(', ', $status), '</li>'; |
|
259 |
+ } |
|
260 |
+ |
|
261 |
+ // Ticket urgency |
|
262 |
+ if (!empty($context['search_params']['urgency'])) |
|
263 |
+ { |
|
264 |
+ $urgency = $context['search_params']['urgency']; |
|
265 |
+ sort($urgency); |
|
266 |
+ foreach ($urgency as $k => $v) |
|
267 |
+ $urgency[$k] = $txt['shd_urgency_' . $v]; |
|
268 |
+ |
|
269 |
+ echo ' |
|
270 |
+ <li>', $txt['shd_search_urgency'], ' ', implode(', ', $urgency), '</li>'; |
|
271 |
+ } |
|
272 |
+ |
|
273 |
+ // Tickets started by |
|
274 |
+ if (!empty($context['search_params']['member_started'])) |
|
275 |
+ { |
|
276 |
+ $members = $context['search_params']['member_started']; |
|
277 |
+ // This is a list of ids we pulled via findMember(). We should have their names having found their ids. |
|
278 |
+ foreach ($members as $k => $v) |
|
279 |
+ $members[$k] = shd_profile_link($context['named_people'][$v], $v); |
|
280 |
+ |
|
281 |
+ echo ' |
|
282 |
+ <li>', $txt['shd_search_ticket_starter'], ' ', implode(', ', $members), '</li>'; |
|
283 |
+ } |
|
284 |
+ |
|
285 |
+ // Tickets assigned |
|
286 |
+ if (!empty($context['search_params']['member_assigned'])) |
|
287 |
+ { |
|
288 |
+ $members = $context['search_params']['member_assigned']; |
|
289 |
+ // This is a list of ids we pulled via findMember(). We should have their names having found their ids. |
|
290 |
+ foreach ($members as $k => $v) |
|
291 |
+ $members[$k] = shd_profile_link($context['named_people'][$v], $v); |
|
292 |
+ |
|
293 |
+ echo ' |
|
294 |
+ <li>', $txt['shd_search_ticket_assignee'], ' ', implode(', ', $members), '</li>'; |
|
295 |
+ } |
|
296 |
+ |
|
297 |
+ echo ' |
|
298 |
+ </ul> |
|
299 |
+ <em>', $txt['shd_search_excluded'], '</em> |
|
300 |
+ </div>'; |
|
301 |
+ } |
|
302 |
+} |
|
303 |
+ |
|
304 |
+function template_search_results() |
|
305 |
+{ |
|
306 |
+ global $context, $txt, $scripturl, $settings, $modSettings, $smcFunc; |
|
307 |
+ |
|
308 |
+ // Back to the helpdesk. |
|
309 |
+ echo ' |
|
310 |
+ <div class="floatleft"> |
|
311 |
+ ', template_button_strip(array($context['navigation']['back'], $context['navigation']['search']), 'bottom'), ' |
|
312 |
+ </div><br class="clear" /><br />'; |
|
313 |
+ |
|
314 |
+ echo ' |
|
315 |
+ <div class="cat_bar"> |
|
316 |
+ <h3 class="catbg"> |
|
317 |
+ <img src="', $settings['default_images_url'], '/simpledesk/search.png" alt="*" /> |
|
318 |
+ ', $txt['shd_search_results'], ' |
|
319 |
+ </h3> |
|
320 |
+ </div>'; |
|
321 |
+ |
|
322 |
+ // Page navigation. It's not your usual page index, and with good reason: we can't use regular links here without risking server hammering. |
|
323 |
+ $num_pages = ceil($context['num_results'] / $context['search_params']['limit']); |
|
324 |
+ $pages = array(); |
|
325 |
+ foreach ($page = $context['numpage'] - 2; $page <= $context['numpage'] + 2; $page++) |
|
326 |
+ $pages[] = $page; |
|
327 |
+ |
|
328 |
+ // The rest of it would go here, in a nice form that carried everything through for next time, with a button named page whose value would be the page number for each page (plus prev/next) you wanted to display |
|
329 |
+ |
|
330 |
+ // Search criteria |
|
331 |
+ template_search_criteria(); |
|
332 |
+ |
|
333 |
+ // And finally, the results themselves. |
|
334 |
+ $use_bg2 = false; |
|
335 |
+ |
|
336 |
+ foreach ($context['search_results'] as $index => $result) |
|
337 |
+ { |
|
338 |
+ echo ' |
|
339 |
+ <div class="search_results_posts"> |
|
340 |
+ <div class="windowbg', $use_bg2 ? '2' : '', ' core_posts"> |
|
341 |
+ <span class="topslice"><span></span></span> |
|
342 |
+ <div class="content flow_auto"> |
|
343 |
+ <div class="topic_details floatleft" style="width: 94%"> |
|
344 |
+ <div class="counter">', $result['result'], '</div> |
|
345 |
+ <h5>', $result['dept_link'], '<a href="', $scripturl, '?action=helpdesk;sa=ticket;ticket=', $result['id_ticket'], '">', sprintf($result['is_ticket'] ? $txt['shd_search_result_ticket'] : $txt['shd_search_result_reply'], $result['display_id']), '</a> - ', $result['subject'], ' (', $txt['shd_search_last_updated'], ' ', timeformat($result['last_updated']), ')</h5> |
|
346 |
+ <span class="smalltext">« <strong>', $result['is_ticket'] ? $txt['shd_search_ticket_opened_by'] : $txt['shd_search_ticket_replied_by'], ' ', shd_profile_link($result['poster_name'], $result['id_member']), '</strong> ', $txt['on'], ' <em>', timeformat($result['poster_time']), '</em> »</span> |
|
347 |
+ </div> |
|
348 |
+ <br class="clear"> |
|
349 |
+ <div class="list_posts double_height">', $result['body'], '</div> |
|
350 |
+ </div> |
|
351 |
+ <span class="botslice"><span></span></span> |
|
352 |
+ </div> |
|
353 |
+ </div>'; |
|
354 |
+ $use_bg2 = !$use_bg2; |
|
355 |
+ } |
|
356 |
+} |
|
357 |
+?> |
|
0 | 358 |
\ No newline at end of file |
1 | 359 |