diff --git a/interface/lib/classes/functions.inc.php b/interface/lib/classes/functions.inc.php index b744e35688c5cb21004195df82ee065d5408d919..2fe719118149305b08570ead903d57b0e32a93d4 100644 --- a/interface/lib/classes/functions.inc.php +++ b/interface/lib/classes/functions.inc.php @@ -109,6 +109,46 @@ class functions { return $url; } + function json_encode($data) { + if(!function_exists('json_encode')){ + switch ($type = gettype($data)){ + case 'NULL': + return 'null'; + case 'boolean': + return ($data ? 'true' : 'false'); + case 'integer': + case 'double': + case 'float': + return $data; + case 'string': + return '"' . addslashes($data) . '"'; + case 'object': + $data = get_object_vars($data); + case 'array': + $output_index_count = 0; + $output_indexed = array(); + $output_associative = array(); + foreach($data as $key => $value){ + $output_indexed[] = $this->json_encode($value); + $output_associative[] = $this->json_encode($key) . ':' . $this->json_encode($value); + if($output_index_count !== NULL && $output_index_count++ !== $key){ + $output_index_count = NULL; + } + } + if($output_index_count !== NULL){ + return '[' . implode(',', $output_indexed) . ']'; + } else { + return '{' . implode(',', $output_associative) . '}'; + } + default: + return ''; // Not supported + } + } else { + return json_encode($data); + } + } + + } diff --git a/interface/web/capp.php b/interface/web/capp.php index 63177341d1d504335a63e2e1b9a6712407de1bd2..45123919880e7f584702dfeaf75ad38595cfa931 100644 --- a/interface/web/capp.php +++ b/interface/web/capp.php @@ -33,6 +33,8 @@ require_once('../lib/app.inc.php'); //* Import module variable $mod = $_REQUEST["mod"]; +//* If we click on a search result, load that one instead of the module's start page +$redirect = (isset($_REQUEST["redirect"]) ? $_REQUEST["redirect"] : ''); //* Check if user is logged in if($_SESSION["s"]["user"]['active'] != 1) { @@ -40,6 +42,8 @@ if($_SESSION["s"]["user"]['active'] != 1) { //die(); } +if(!preg_match("/^[a-z]{2,20}$/i", $mod)) die('module name contains unallowed chars.'); + //* Check if user may use the module. $user_modules = explode(",",$_SESSION["s"]["user"]["modules"]); @@ -50,7 +54,12 @@ if(is_file($mod."/lib/module.conf.php")) { include_once($mod."/lib/module.conf.php"); $_SESSION["s"]["module"] = $module; session_write_close(); - echo "HEADER_REDIRECT:".$_SESSION["s"]["module"]["startpage"]; + if($redirect == ''){ + echo "HEADER_REDIRECT:".$_SESSION["s"]["module"]["startpage"]; + } else { + //* If we click on a search result, load that one instead of the module's start page + echo "HEADER_REDIRECT:".$redirect; + } } else { $app->error($app->lng(302)); } diff --git a/interface/web/client/form/client.tform.php b/interface/web/client/form/client.tform.php index 85b867104f2efd37604f7d5f4750d3da84d48df4..3fca605ef9c447f3c0ba7321c17eb7a7f53712b4 100644 --- a/interface/web/client/form/client.tform.php +++ b/interface/web/client/form/client.tform.php @@ -29,6 +29,11 @@ Hint: The ID field of the database table is not part of the datafield definition. The ID field must be always auto incement (int or bigint). + + Search: + - searchable = 1 or searchable = 2 include the field in the search + - searchable = 1: this field will be the title of the search result + - searchable = 2: this field will be included in the description of the search result */ @@ -90,7 +95,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'contact_name' => array ( 'datatype' => 'VARCHAR', @@ -104,7 +110,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 1 ), 'customer_no' => array ( 'datatype' => 'VARCHAR', @@ -115,7 +122,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'username' => array ( 'datatype' => 'VARCHAR', @@ -136,7 +144,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'password' => array ( 'datatype' => 'VARCHAR', @@ -181,7 +190,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'zip' => array ( 'datatype' => 'VARCHAR', @@ -192,7 +202,8 @@ $form["tabs"]['address'] = array ( 'width' => '10', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'city' => array ( 'datatype' => 'VARCHAR', @@ -203,7 +214,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'state' => array ( 'datatype' => 'VARCHAR', @@ -214,7 +226,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'country' => array ( 'datatype' => 'VARCHAR', @@ -237,7 +250,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'mobile' => array ( 'datatype' => 'VARCHAR', @@ -248,7 +262,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'fax' => array ( 'datatype' => 'VARCHAR', @@ -259,7 +274,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'email' => array ( 'datatype' => 'VARCHAR', @@ -270,7 +286,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'internet' => array ( 'datatype' => 'VARCHAR', @@ -281,7 +298,8 @@ $form["tabs"]['address'] = array ( 'width' => '30', 'maxlength' => '255', 'rows' => '', - 'cols' => '' + 'cols' => '', + 'searchable' => 2 ), 'icq' => array ( 'datatype' => 'VARCHAR', diff --git a/interface/web/dashboard/ajax_get_json.php b/interface/web/dashboard/ajax_get_json.php new file mode 100644 index 0000000000000000000000000000000000000000..18cd1e45e8bc57bd748fd736f581288c2315b22d --- /dev/null +++ b/interface/web/dashboard/ajax_get_json.php @@ -0,0 +1,197 @@ +auth->check_module_permissions('dashboard'); + +$app->uses('tform'); + +$type = $_GET["type"]; + +//if($_SESSION["s"]["user"]["typ"] == 'admin') { + + + if($type == 'globalsearch'){ + $q = $app->db->quote($_GET["q"]); + $authsql = " AND ".$app->tform->getAuthSQL('r'); + $modules = explode(',', $_SESSION['s']['user']['modules']); + + // clients + $result_clients = _search('client', 'client'); + + // web sites + $result_webs = _search('sites', 'web_domain'); + + // FTP users + $result_ftp_users = _search('sites', 'ftp_user'); + + // shell users + $result_shell_users = _search('sites', 'shell_user'); + + // databases + /* + $result_databases = array('cheader' => array(), 'cdata' => array()); + if(in_array('sites', $modules)){ + $sql = "SELECT * FROM web_database WHERE database_name LIKE '%".$q."%' OR database_user LIKE '%".$q."%' OR remote_ips LIKE '%".$q."%'".$authsql." ORDER BY database_name"; + $results = $app->db->queryAllRecords($sql); + + if(is_array($results) && !empty($results)){ + $result_databases['cheader'] = array('title' => 'Databases', + 'total' => count($results), + 'limit' => count($results) + ); + foreach($results as $result){ + $description = 'Database User: '.$result['database_user'].' - Remote IPs: '.$result['remote_ips']; + $result_databases['cdata'][] = array('title' => $result['database_name'], + 'description' => $description, + 'onclick' => 'capp(\'sites\',\'sites/database_edit.php?id='.$result['database_id'].'\');', + 'fill_text' => strtolower($result['database_name']) + ); + } + } + } + */ + $result_databases = _search('sites', 'database'); + + // email domains + $result_email_domains = _search('mail', 'mail_domain'); + + // email mailboxes + $result_email_mailboxes = _search('mail', 'mail_user'); + + // dns zones + $result_dns_zones = _search('dns', 'dns_soa'); + + // secondary dns zones + $result_secondary_dns_zones = _search('dns', 'dns_slave'); + + // virtual machines + $result_vms = _search('vm', 'openvz_vm'); + + // virtual machines os templates + $result_vm_ostemplates = _search('vm', 'openvz_ostemplate'); + + // virtual machines vm templates + $result_vm_vmtemplates = _search('vm', 'openvz_template'); + + // virtual machines ip addresses + $result_vm_ip_addresses = _search('vm', 'openvz_ip'); + + $json = $app->functions->json_encode(array($result_clients, $result_webs, $result_ftp_users, $result_shell_users, $result_databases, $result_email_domains, $result_email_mailboxes, $result_dns_zones, $result_secondary_dns_zones, $result_vms, $result_vm_ostemplates, $result_vm_vmtemplates, $result_vm_ip_addresses)); + } + +//} + +function _search($module, $section){ + global $app, $q, $authsql, $modules; + //$q = $app->db->quote($_GET["q"]); + //$authsql = " AND ".$app->tform->getAuthSQL('r'); + //$user_modules = explode(',', $_SESSION['s']['user']['modules']); + + $result_array = array('cheader' => array(), 'cdata' => array()); + if(in_array($module, $modules)){ + $search_fields = array(); + $desc_fields = array(); + if(is_file('../'.$module.'/form/'.$section.'.tform.php')){ + include_once('../'.$module.'/form/'.$section.'.tform.php'); + + $category_title = $form["title"]; + $form_file = $form["action"]; + $db_table = $form["db_table"]; + $db_table_idx = $form["db_table_idx"]; + $order_by = $db_table_idx; + + if(is_array($form["tabs"]) && !empty($form["tabs"])){ + foreach($form["tabs"] as $tab){ + if(is_array($tab['fields']) && !empty($tab['fields'])){ + foreach($tab['fields'] as $key => $val){ + if(isset($val['searchable']) && $val['searchable'] > 0){ + $search_fields[] = $key." LIKE '%".$q."%'"; + if($val['searchable'] == 1){ + $order_by = $key; + $title_key = $key; + } + if($val['searchable'] == 2){ + $desc_fields[] = $key; + } + } + } + } + } + } + } + unset($form); + + $where_clause = ''; + if(!empty($search_fields)){ + $where_clause = implode(' OR ', $search_fields); + } else { + // valid SQL query which returns an empty result set + $where_clause = '1 = 0'; + } + $order_clause = ''; + if($order_by != '') $order_clause = ' ORDER BY '.$order_by; + + $results = $app->db->queryAllRecords("SELECT * FROM ".$db_table." WHERE ".$where_clause.$authsql.$order_clause); + + if(is_array($results) && !empty($results)){ + $lng_file = '../'.$module.'/lib/lang/'.$_SESSION['s']['language'].'_'.$section.'.lng'; + if(is_file($lng_file)) include($lng_file); + $result_array['cheader'] = array('title' => $category_title, + 'total' => count($results), + 'limit' => count($results) + ); + foreach($results as $result){ + $description = ''; + if(!empty($desc_fields)){ + $desc_items = array(); + foreach($desc_fields as $desc_field){ + if($result[$desc_field] != '') $desc_items[] = $wb[$desc_field.'_txt'].': '.$result[$desc_field]; + } + if(!empty($desc_items)) $description = implode(' - ', $desc_items); + } + + $result_array['cdata'][] = array('title' => $wb[$title_key.'_txt'].': '.$result[$title_key], + 'description' => $description, + 'onclick' => "capp('".$module."','".$module."/".$form_file."?id=".$result[$db_table_idx]."');", + 'fill_text' => strtolower($result[$title_key]) + ); + } + } + } + return $result_array; +} + +header('Content-type: application/json'); +echo $json; +?> \ No newline at end of file diff --git a/interface/web/dns/form/dns_slave.tform.php b/interface/web/dns/form/dns_slave.tform.php index 097602e8e5f88faab9d2047e10334dc85f4e83a1..a1dea941fc34999eff34c058b1b074f784b54f9c 100644 --- a/interface/web/dns/form/dns_slave.tform.php +++ b/interface/web/dns/form/dns_slave.tform.php @@ -29,6 +29,11 @@ Hint: The ID field of the database table is not part of the datafield definition. The ID field must be always auto incement (int or bigint). + + Search: + - searchable = 1 or searchable = 2 include the field in the search + - searchable = 1: this field will be the title of the search result + - searchable = 2: this field will be included in the description of the search result */ @@ -88,7 +93,8 @@ $form["tabs"]['dns_slave'] = array ( 'default' => '', 'value' => '', 'width' => '30', - 'maxlength' => '255' + 'maxlength' => '255', + 'searchable' => 1 ), 'ns' => array ( 'datatype' => 'VARCHAR', @@ -100,7 +106,8 @@ $form["tabs"]['dns_slave'] = array ( 'default' => '', 'value' => '', 'width' => '30', - 'maxlength' => '255' + 'maxlength' => '255', + 'searchable' => 2 ), 'xfer' => array ( 'datatype' => 'VARCHAR', diff --git a/interface/web/dns/form/dns_soa.tform.php b/interface/web/dns/form/dns_soa.tform.php index 53b408ef3a1cab68a3a48c977815de7ec5765cd4..c102e126fc00f36082536305705cfad9f9a92366 100644 --- a/interface/web/dns/form/dns_soa.tform.php +++ b/interface/web/dns/form/dns_soa.tform.php @@ -29,6 +29,11 @@ Hint: The ID field of the database table is not part of the datafield definition. The ID field must be always auto incement (int or bigint). + + Search: + - searchable = 1 or searchable = 2 include the field in the search + - searchable = 1: this field will be the title of the search result + - searchable = 2: this field will be included in the description of the search result */ @@ -88,7 +93,8 @@ $form["tabs"]['dns_soa'] = array ( 'default' => '', 'value' => '', 'width' => '30', - 'maxlength' => '255' + 'maxlength' => '255', + 'searchable' => 1 ), 'ns' => array ( 'datatype' => 'VARCHAR', @@ -100,7 +106,8 @@ $form["tabs"]['dns_soa'] = array ( 'default' => '', 'value' => '', 'width' => '30', - 'maxlength' => '255' + 'maxlength' => '255', + 'searchable' => 2 ), 'mbox' => array ( 'datatype' => 'VARCHAR', @@ -114,7 +121,8 @@ $form["tabs"]['dns_soa'] = array ( 'default' => '', 'value' => '', 'width' => '30', - 'maxlength' => '255' + 'maxlength' => '255', + 'searchable' => 2 ), 'serial' => array ( 'datatype' => 'INTEGER', diff --git a/interface/web/js/jquery.ispconfigsearch.js b/interface/web/js/jquery.ispconfigsearch.js new file mode 100644 index 0000000000000000000000000000000000000000..211dcd76dc7eea784694ec8ece516b402a43c11b --- /dev/null +++ b/interface/web/js/jquery.ispconfigsearch.js @@ -0,0 +1,156 @@ +/* +Copyright (c) 2012, ISPConfig UG +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +(function($) { + $.fn.ispconfigSearch = function(settings){ + + var defaultSettings = { + dataSrc: '', + timeout: 500, + minChars: 2, + resultBox: '-resultbox', + cssPrefix: 'gs-', + fillSearchField: false, + fillSearchFieldWith: 'title', + resultsText: '$ of % results', + noResultsText: 'No results.', + displayEmptyCategories: false, + runJS: true + }; + + var previousQ = ''; + var data; + var settings = $.extend(defaultSettings, settings); + settings.resultBox = $(this).attr('id')+settings.resultBox; + + $(this).attr('autocomplete', 'off'); + $(this).wrap('
'); + $(this).after(' '); + var searchField = $(this); + var resultBox = $('#'+settings.resultBox); + + var timeout = null; + searchField.keyup(function(event) { + // 13 = enter, 9 = tab + if (event.keyCode != 13 && event.keyCode != 9){ + // query value + var q = searchField.val(); + + if (settings.minChars > q.length || q == ''){ + resultBox.fadeOut(); + resetTimer(timeout); + } else { + if (timeout != null){ + resetTimer(timeout); + } + + timeout = setTimeout(function(){ + searchField.addClass(settings.cssPrefix+'loading'); + + // we don't have to perform a new search if the query equals the previous query + previousQ = q; + var queryStringCombinator = '?'; + if(settings.dataSrc.indexOf('?') != -1){ + queryStringCombinator = '&'; + } + $.getJSON(settings.dataSrc+queryStringCombinator+"q="+q, function(data, textStatus){ + if (textStatus == 'success'){ + var output = ''; + var resultsFound = false; + + if($.isEmptyObject(data) === false){ + $.each(data, function(i, category){ + if (category['cdata'].length > 0){ + resultsFound = true; + } + }); + } + + if (!resultsFound){ + output += ''+settings.noResultsText+'
0 results
'+category['cheader']['title']+'
'+settings.resultsText.replace("%", category['cheader']['total']).replace("$", (category['cheader']['limit'] < category['cdata'].length ? category['cheader']['limit'] : category['cdata'].length))+'
';
+ output += ' ';
+ output += (item['title'] != undefined) ? ''+item['title']+" |