diff --git a/lib/Froxlor/Ajax/Ajax.php b/lib/Froxlor/Ajax/Ajax.php index 4de5c213..d8b3cb05 100644 --- a/lib/Froxlor/Ajax/Ajax.php +++ b/lib/Froxlor/Ajax/Ajax.php @@ -6,6 +6,7 @@ use Exception; use Froxlor\Http\HttpClient; use Froxlor\PhpHelper; use Froxlor\Settings; +use Froxlor\User; use Froxlor\UI\Panel\UI; use Froxlor\UI\Request; @@ -29,6 +30,7 @@ class Ajax protected string $session; protected string $action; protected string $theme; + protected array $userinfo; protected array $lng; /** @@ -40,6 +42,17 @@ class Ajax $this->action = $_GET['action'] ?? $_POST['action'] ?? null; $this->theme = $_GET['theme'] ?? 'Froxlor'; + UI::sendHeaders(); + UI::sendSslHeaders(); + + ini_set("session.name", "s"); + ini_set("url_rewriter.tags", ""); + ini_set("session.use_cookies", false); + ini_set("session.cookie_httponly", true); + ini_set("session.cookie_secure", UI::$SSL_REQ); + session_id($this->session); + session_start(); + $this->initLang(); } @@ -97,15 +110,15 @@ class Ajax */ public function handle() { - $session = $this->getValidatedSession(); + $this->userinfo = $this->getValidatedSession(); switch ($this->action) { case 'newsfeed': return $this->getNewsfeed(); case 'updatecheck': return $this->getUpdateCheck(); - case 'searchsetting': - return $this->searchSetting(); + case 'searchglobal': + return $this->searchGlobal(); default: return $this->errorResponse('Action not found!'); } @@ -135,9 +148,9 @@ class Ajax $timediff = time() - \Froxlor\Settings::Get('session.sessiontimeout'); $sel_stmt = \Froxlor\Database\Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_SESSIONS . "` - WHERE `hash` = :hash AND `ipaddress` = :ipaddr AND `useragent` = :ua AND `lastactivity` > :timediff - "); + SELECT * FROM `" . TABLE_PANEL_SESSIONS . "` + WHERE `hash` = :hash AND `ipaddress` = :ipaddr AND `useragent` = :ua AND `lastactivity` > :timediff + "); $session = \Froxlor\Database\Database::pexecute_first($sel_stmt, [ 'hash' => $this->session, @@ -150,7 +163,27 @@ class Ajax throw new Exception('Session is not defined!'); } - return $session; + if ($session['adminsession'] == 1) { + // test for admin + $sel_stmt = \Froxlor\Database\Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_ADMINS . "` + WHERE `adminid` = :userid + "); + } else { + // test for customer + $sel_stmt = \Froxlor\Database\Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `customerid` = :userid + "); + } + $user = \Froxlor\Database\Database::pexecute_first($sel_stmt, [ + 'userid' => $session['userid'] + ]); + if (!$user) { + throw new Exception('Session is not defined!'); + } + $user['adminsession'] = $session['adminsession']; + return $user; } /** @@ -237,34 +270,96 @@ class Ajax } } - private function searchSetting() + /** + * @todo $userinfo + */ + private function searchGlobal() { $searchtext = Request::get('searchtext'); $result = []; - if ($searchtext && strlen($searchtext) > 2) { - $settings_data = PhpHelper::loadConfigArrayDir(\Froxlor\Froxlor::getInstallDir() . '/actions/admin/settings/'); - $results = array(); - PhpHelper::recursive_array_search($searchtext, $settings_data, $results); - $processed_setting = array(); - foreach ($results as $pathkey) { - $pk = explode(".", $pathkey); - if (count($pk) > 4) { - $settingkey = $pk[0] . '.' . $pk[1] . '.' . $pk[2] . '.' . $pk[3]; - if (!array_key_exists($settingkey, $processed_setting)) { - $processed_setting[$settingkey] = true; - $sresult = $settings_data[$pk[0]][$pk[1]][$pk[2]][$pk[3]]; - if ($sresult['type'] != 'hidden') { - $result[] = [ - 'title' => (is_array($sresult['label']) ? $sresult['label']['title'] : $sresult['label']), - 'href' => 'admin_settings.php?page=overview&part=' . $pk[1] . '&em=' . $pk[3] . '&s=' . $this->session, - ]; + if ($searchtext && strlen(trim($searchtext)) > 2) { + + $processed = []; + + $stparts = explode(" ", $searchtext); + + foreach ($stparts as $searchtext) { + $searchtext = trim($searchtext); + + // settings (if allowed) + if (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1) { + + if ($this->userinfo['change_serversettings'] == 1) { + $settings_data = PhpHelper::loadConfigArrayDir(\Froxlor\Froxlor::getInstallDir() . '/actions/admin/settings/'); + $results = array(); + if (!isset($processed['settings'])) { + $processed['settings'] = []; + } + PhpHelper::recursive_array_search($searchtext, $settings_data, $results); + foreach ($results as $pathkey) { + $pk = explode(".", $pathkey); + if (count($pk) > 4) { + $settingkey = $pk[0] . '.' . $pk[1] . '.' . $pk[2] . '.' . $pk[3]; + if (is_array($processed['settings']) && !array_key_exists($settingkey, $processed['settings'])) { + $processed['settings'][$settingkey] = true; + $sresult = $settings_data[$pk[0]][$pk[1]][$pk[2]][$pk[3]]; + if ($sresult['type'] != 'hidden') { + $result[] = [ + 'title' => (is_array($sresult['label']) ? $sresult['label']['title'] : $sresult['label']), + 'href' => 'admin_settings.php?page=overview&part=' . $pk[1] . '&em=' . $pk[3] . '&s=' . $this->session, + 'category' => 'settings' + ]; + } + } + } } } - } - } + + // customers + $searchfields = [ + 'c.loginname', + 'c.name', + 'c.firstname', + 'c.company', + 'c.street', + 'c.zipcode', + 'c.city', + 'c.email', + 'c.customernumber' + ]; + $collection = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Customers::class, $this->userinfo)) + ->addParam(['sql_search' => [ + '_plainsql' => $this->searchStringSql($searchfields, $searchtext) + ]]); + if ($collection->count() > 0) { + if (!isset($processed['customer'])) { + $processed['customer'] = []; + } + foreach ($collection->getList() as $cresult) { + if (is_array($processed['customer']) && !array_key_exists($cresult['customerid'], $processed['customer'])) { + $processed['customer'][$cresult['customerid']] = true; + $result[] = [ + 'title' => User::getCorrectFullUserDetails($cresult), + 'href' => 'admin_customers.php?page=customers&action=edit&id=' . $cresult['customerid'] . '&s=' . $this->session, + 'category' => 'customer' + ]; + } + } + } + } // is-admin + } // foreach splitted search-term } header("Content-type: application/json"); - echo json_encode(['settings' => $result]); + echo json_encode($result); + } + + private function searchStringSql(array $searchfields, $searchtext) + { + $result = "("; + foreach ($searchfields as $sf) { + $result .= $sf . " LIKE " . \Froxlor\Database\Database::quote('%' . $searchtext . '%') . " OR "; + } + return substr($result, 0, -3) . ")"; } } diff --git a/lib/Froxlor/Api/ApiCommand.php b/lib/Froxlor/Api/ApiCommand.php index c2d646e8..b2b0b994 100644 --- a/lib/Froxlor/Api/ApiCommand.php +++ b/lib/Froxlor/Api/ApiCommand.php @@ -1,4 +1,5 @@ dbversion = \Froxlor\Froxlor::DBVERSION; $this->branding = \Froxlor\Froxlor::BRANDING; - if (! empty($header)) { + if (!empty($header)) { $this->readUserData($header); - } elseif (! empty($userinfo)) { + } elseif (!empty($userinfo)) { $this->user_data = $userinfo; $this->is_admin = (isset($userinfo['adminsession']) && $userinfo['adminsession'] == 1 && $userinfo['adminid'] > 0) ? true : false; } else { @@ -272,7 +273,7 @@ abstract class ApiCommand extends ApiParameter { $search = $this->getParam('sql_search', true, array()); $condition = ''; - if (! empty($search)) { + if (!empty($search)) { if ($append == true) { $condition = ' AND '; } else { @@ -285,43 +286,47 @@ abstract class ApiCommand extends ApiParameter ); $first = true; foreach ($search as $field => $valoper) { - $cleanfield = str_replace(".", "", $field); - $sortfield = explode('.', $field); - foreach ($sortfield as $id => $sfield) { - if (substr($sfield, - 1, 1) != '`') { - $sfield .= '`'; - } - if ($sfield[0] != '`') { - $sfield = '`' . $sfield; - } - $sortfield[$id] = $sfield; - } - $field = implode('.', $sortfield); - if (! $first) { - $condition .= ' AND '; - } - if (! is_array($valoper) || ! isset($valoper['op']) || empty($valoper['op'])) { - $condition .= $field . ' LIKE :' . $cleanfield; - if (! is_array($valoper)) { - $query_fields[':' . $cleanfield] = '%' . $valoper . '%'; - } else { - $query_fields[':' . $cleanfield] = '%' . $valoper['value'] . '%'; - } - } elseif (in_array($valoper['op'], $ops)) { - $condition .= $field . ' ' . $valoper['op'] . ':' . $cleanfield; - $query_fields[':' . $cleanfield] = $valoper['value'] ?? ''; - } elseif (strtolower($valoper['op']) == 'in' && is_array($valoper['value']) && count($valoper['value']) > 0) { - $condition .= $field . ' ' . $valoper['op'] . ' ('; - foreach ($valoper['value'] as $incnt => $invalue) { - $condition .= ":" . $cleanfield . $incnt . ", "; - $query_fields[':' . $cleanfield . $incnt] = $invalue ?? ''; - } - $condition = substr($condition, 0, - 2) . ')'; + if ($field == '_plainsql') { + $condition .= $valoper; } else { - continue; - } - if ($first) { - $first = false; + $cleanfield = str_replace(".", "", $field); + $sortfield = explode('.', $field); + foreach ($sortfield as $id => $sfield) { + if (substr($sfield, -1, 1) != '`') { + $sfield .= '`'; + } + if ($sfield[0] != '`') { + $sfield = '`' . $sfield; + } + $sortfield[$id] = $sfield; + } + $field = implode('.', $sortfield); + if (!$first) { + $condition .= ' AND '; + } + if (!is_array($valoper) || !isset($valoper['op']) || empty($valoper['op'])) { + $condition .= $field . ' LIKE :' . $cleanfield; + if (!is_array($valoper)) { + $query_fields[':' . $cleanfield] = '%' . $valoper . '%'; + } else { + $query_fields[':' . $cleanfield] = '%' . $valoper['value'] . '%'; + } + } elseif (in_array($valoper['op'], $ops)) { + $condition .= $field . ' ' . $valoper['op'] . ':' . $cleanfield; + $query_fields[':' . $cleanfield] = $valoper['value'] ?? ''; + } elseif (strtolower($valoper['op']) == 'in' && is_array($valoper['value']) && count($valoper['value']) > 0) { + $condition .= $field . ' ' . $valoper['op'] . ' ('; + foreach ($valoper['value'] as $incnt => $invalue) { + $condition .= ":" . $cleanfield . $incnt . ", "; + $query_fields[':' . $cleanfield . $incnt] = $invalue ?? ''; + } + $condition = substr($condition, 0, -2) . ')'; + } else { + continue; + } + if ($first) { + $first = false; + } } } } @@ -343,10 +348,10 @@ abstract class ApiCommand extends ApiParameter $limit = $this->getParam('sql_limit', true, 0); $offset = $this->getParam('sql_offset', true, 0); - if (! is_numeric($limit)) { + if (!is_numeric($limit)) { $limit = 0; } - if (! is_numeric($offset)) { + if (!is_numeric($offset)) { $offset = 0; } @@ -371,7 +376,7 @@ abstract class ApiCommand extends ApiParameter { $orderby = $this->getParam('sql_orderby', true, array()); $order = ""; - if (! empty($orderby)) { + if (!empty($orderby)) { if ($append) { $order .= ", "; } else { @@ -389,7 +394,7 @@ abstract class ApiCommand extends ApiParameter foreach ($orderby as $field => $by) { $sortfield = explode('.', $field); foreach ($sortfield as $id => $sfield) { - if (substr($sfield, - 1, 1) != '`') { + if (substr($sfield, -1, 1) != '`') { $sfield .= '`'; } if ($sfield[0] != '`') { @@ -399,7 +404,7 @@ abstract class ApiCommand extends ApiParameter } $field = implode('.', $sortfield); $by = strtoupper($by); - if (! in_array($by, [ + if (!in_array($by, [ 'ASC', 'DESC' ])) { @@ -417,7 +422,7 @@ abstract class ApiCommand extends ApiParameter $order .= $field . " " . $by . ", "; } } - $order = substr($order, 0, - 2); + $order = substr($order, 0, -2); } return $order; @@ -463,16 +468,16 @@ abstract class ApiCommand extends ApiParameter return json_decode($json_result, true)['data']; } - /** - * return api-compatible response in JSON format and send corresponding http-header - * - * @param mixed $data - * @param int $response_code - * @return string json-encoded response message - */ + /** + * return api-compatible response in JSON format and send corresponding http-header + * + * @param mixed $data + * @param int $response_code + * @return string json-encoded response message + */ protected function response($data = null, int $response_code = 200) { - return \Froxlor\Api\Response::jsonDataResponse($data, $response_code); + return \Froxlor\Api\Response::jsonDataResponse($data, $response_code); } /** @@ -493,7 +498,7 @@ abstract class ApiCommand extends ApiParameter $customerid = $this->getParam('customerid', true, 0); $loginname = $this->getParam('loginname', true, ''); - if (! empty($customerid) || ! empty($loginname)) { + if (!empty($customerid) || !empty($loginname)) { $_result = $this->apiCall('Customers.get', array( 'id' => $customerid, 'loginname' => $loginname @@ -509,7 +514,7 @@ abstract class ApiCommand extends ApiParameter $customer_ids[] = $customer['customerid']; } } else { - if (! $this->isInternal() && ! empty($customer_hide_option) && \Froxlor\Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { + if (!$this->isInternal() && !empty($customer_hide_option) && \Froxlor\Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { throw new \Exception("You cannot access this resource", 405); } $customer_ids = array( @@ -545,7 +550,7 @@ abstract class ApiCommand extends ApiParameter 'loginname' => $loginname )); // check whether the customer has enough resources - if (! empty($customer_resource_check) && $customer[$customer_resource_check . '_used'] >= $customer[$customer_resource_check] && $customer[$customer_resource_check] != '-1') { + if (!empty($customer_resource_check) && $customer[$customer_resource_check . '_used'] >= $customer[$customer_resource_check] && $customer[$customer_resource_check] != '-1') { throw new \Exception("Customer has no more resources available", 406); } } else { diff --git a/lib/ajax.php b/lib/ajax.php index d4b81660..f86b1f93 100644 --- a/lib/ajax.php +++ b/lib/ajax.php @@ -1,11 +1,9 @@ handle(); + echo (new Ajax)->handle(); } catch (Exception $e) { - echo \Froxlor\Api\Response::jsonErrorResponse($e->getMessage(), 500); + echo \Froxlor\Api\Response::jsonErrorResponse($e->getMessage(), 500); } diff --git a/templates/Froxlor/src/js/components/search.js b/templates/Froxlor/src/js/components/search.js index af3982b8..3663339b 100644 --- a/templates/Froxlor/src/js/components/search.js +++ b/templates/Froxlor/src/js/components/search.js @@ -1,30 +1,20 @@ $(document).ready(function () { console.log('included search'); - $.typeahead({ - input: '.js-typeahead-search_v1', - order: "desc", - dynamic: true, - display: ['title'], - href: "{{href}}", - emptyTemplate: "No results for {{query}}", - debug: true, - source: { - settings: { - ajax: { - method: "post", - url: "lib/ajax.php?action=searchsetting&theme=" + window.$theme + "&s=" + window.$session, - path: "settings", - data: { - searchtext: '{{query}}' - }, - } + $('input[class=js-typeahead-search_v1]').on('change keyup keydown', function () { + $.ajax({ + url: "lib/ajax.php?action=searchglobal&theme=" + window.$theme + "&s=" + window.$session, + type: "POST", + data: { + searchtext: $(this).val() }, - }, - callback: { - onInit: function (node) { - console.log('Typeahead Initiated'); + dataType: "json", + success: function (data) { + console.log(data); + }, + error: function (a, b) { + console.log(a, b); } - } + }); }); });