From 2eafb8b22e8123d1cd8674d2d4faf85d76e5281e Mon Sep 17 00:00:00 2001 From: RussH Date: Thu, 3 Sep 2015 17:08:18 +0100 Subject: [PATCH 1/7] Create README.MD --- optional-updates/latest-sphinx-search/README.MD | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 optional-updates/latest-sphinx-search/README.MD diff --git a/optional-updates/latest-sphinx-search/README.MD b/optional-updates/latest-sphinx-search/README.MD new file mode 100644 index 000000000..f1ae300f1 --- /dev/null +++ b/optional-updates/latest-sphinx-search/README.MD @@ -0,0 +1,8 @@ +Installation instructions for installing the latest Sphinx Search are here +https://github.com/opencats/OpenCATS/wiki/Latest-Sphinx-search + +Please execute this AFTER a normal OpenCATS installation to update Sphinx. +Note: at some future point exisitng 'install Sphinx' instrucitons will be replaced with this verison in its entirety. + +The attached sample files in this directory are the sample configuration files that are referred to in the above wiki article. + From f660765d578b40014b66aa0e7a7f38ccbf1625b4 Mon Sep 17 00:00:00 2001 From: "russellh@ysmail.net" Date: Thu, 3 Sep 2015 19:31:40 +0100 Subject: [PATCH 2/7] Signed-off-by: russellh@ysmail.net --- .../latest-sphinx-search/Search.php | 2487 +++++++++++++++++ .../latest-sphinx-search/config.php | 249 ++ .../latest-sphinx-search/sphinx.conf | 76 + 3 files changed, 2812 insertions(+) create mode 100644 optional-updates/latest-sphinx-search/Search.php create mode 100644 optional-updates/latest-sphinx-search/config.php create mode 100644 optional-updates/latest-sphinx-search/sphinx.conf diff --git a/optional-updates/latest-sphinx-search/Search.php b/optional-updates/latest-sphinx-search/Search.php new file mode 100644 index 000000000..2a1e794d8 --- /dev/null +++ b/optional-updates/latest-sphinx-search/Search.php @@ -0,0 +1,2487 @@ + $key) + { + if ($length >= SEARCH_EXCERPT_LENGTH) + { + break; + } + + /* Escape the key for use with preg_*(). */ + $key = preg_quote($key, '/'); + + /* Remember occurrence of key so we can skip over it if more occurrnces + * are desired. + */ + if (!isset($included[$key])) + { + $included[$key] = 0; + } + + $regExPass = false; + + /* Check for wildcards */ + if (strpos($key, '*') !== false) + { + $newKey = str_replace('\*', '', $key); + $regExPass = preg_match( + '/' . $newKey . '/i', $text, $matches, + PREG_OFFSET_CAPTURE, $included[$key] + ); + } + else + { + $regExPass = preg_match( + '/\b' . $key . '\b/i', $text, $matches, + PREG_OFFSET_CAPTURE, $included[$key] + ); + } + + if ($regExPass) + { + $firstMatchOffset = $matches[0][1]; + + $firstSpaceInRange = strpos($text, ' ', max(0, $firstMatchOffset - 60)); + if ($firstSpaceInRange !== false) + { + $end = substr($text, $firstMatchOffset, 80); + $lastSpaceInRange = strrpos($end, ' '); + + if ($lastSpaceInRange !== false) + { + $ranges[$firstSpaceInRange] = $firstMatchOffset + $lastSpaceInRange; + $length += $firstMatchOffset + $lastSpaceInRange - $firstSpaceInRange; + $included[$key] = $firstMatchOffset + 1; + } + else + { + unset($workingKeys[$keyOffset]); + } + } + else + { + unset($workingKeys[$keyOffset]); + } + } + else + { + unset($workingKeys[$keyOffset]); + } + } + } + + /* If we didn't find anything, return the beginning of the text up to + * SEARCH_EXCERPT_LENGTH. + */ + if (sizeof($ranges) == 0) + { + + $text = DatabaseSearch::fulltextDecode($text); + return substr($text, 0, SEARCH_EXCERPT_LENGTH); + } + + /* Sort the text ranges by starting position. */ + ksort($ranges); + + /* For each range, in the $ranges array, compare to every other range + * and test for overlapping ranges. Merge overlapping ranges togeather. + * The ksort()ing makes this O(n). + */ + $newRanges = array(); + foreach ($ranges as $rangeFrom => $rangeTo) + { + /* On the first loop, set the 'base range' to the first range's + * limits and continue on to the next loop. + */ + if (!isset($baseRangeFrom)) + { + $baseRangeFrom = $rangeFrom; + $baseRangeTo = $rangeTo; + + continue; + } + + /* If the start of the current range is before the end of the + * previous range, make the 'base range' include the new range as + * well. Otherwise, start the 'base range' over at the limits for + * the current range. + */ + if ($rangeFrom <= $baseRangeTo) + { + $baseRangeTo = max($baseRangeTo, $rangeTo); + } + else + { + /* Every time we start the 'base range' over, store the + * previous combined range that we just calculated in the + * 'new ranges' array. + */ + $newRanges[$baseRangeFrom] = $baseRangeTo; + + $baseRangeFrom = $rangeFrom; + $baseRangeTo = $rangeTo; + } + } + + /* Store the last combined range that we just calculated in the 'new + * ranges' array. + */ + $newRanges[$baseRangeFrom] = $baseRangeTo; + + /* Fetch text. */ + $out = array(); + foreach ($newRanges as $from => $to) + { + $out[] = substr($text, $from, $to - $from); + } + + $text = implode(' ... ', $out); + + /* Highlight wildcards differently. */ + $keywordsWild = array(); + foreach ($keywords as $keyOffset => $key) + { + if (strpos($key, '*') !== false) + { + $keywordsWild[] = str_replace('*', '', $key); + unset($keywords[$keyOffset]); + } + } + $keywords = array_merge($keywords); + + if (!empty($keywordsWild)) + { + $regex = implode('|', array_map( + create_function('$string','return preg_quote($string, \'/\');'), $keywordsWild + )); + $text = preg_replace( + '/(' . $regex . ')/i', + '\1', + $text + ); + } + + if (!empty($keywords)) + { + $regex = implode('|', array_map( + create_function('$string','return preg_quote($string, \'/\');'), $keywords + )); + $text = preg_replace( + '/\b(' . $regex . ')\b/i', + '\1', + $text + ); + } + + if (isset($newRanges[0])) + { + $text = $text . ' ...'; + } + else + { + $text = '... ' . $text . ' ...'; + } + + + /* Remove AntiWord 'table bars' */ + $text = str_replace('|', '', $text); + + return DatabaseSearch::fulltextDecode($text); + } + + /** + * Highlights keywords in text for a resume preview and preforms CATS + * fulltext decoding. + * + * @param array keywords to highlight + * @param string resume text + * @return string highlighted preview text + */ + public static function makePreview($keywords, $text) + { + if (empty($keywords)) + { + return DatabaseSearch::fulltextDecode($text); + } + + /* CATS fulltext encode the search string. */ + $keywords = DatabaseSearch::fulltextEncode($keywords); + + /* Create an array of keywords to highlight. */ + $keywords = self::makeKeywordsArray($keywords); + + /* Highlight wildcards differently. */ + $keywordsWild = array(); + foreach ($keywords as $keyOffset => $key) + { + if (strpos($key, '*') !== false) + { + $keywordsWild[] = str_replace('*', '', $key); + unset($keywords[$keyOffset]); + } + } + $keywords = array_merge($keywords); + + if (!empty($keywordsWild)) + { + $regex = implode('|', array_map( + create_function('$string','return preg_quote($string, \'/\');'), $keywordsWild + )); + $text = preg_replace( + '/(' . $regex . ')/i', + '\1', + $text + ); + } + + if (!empty($keywords)) + { + $regex = implode('|', array_map( + create_function('$string','return preg_quote($string, \'/\');'), $keywords + )); + $text = preg_replace( + '/\b(' . $regex . ')\b/i', + '\1', + $text + ); + } + + return DatabaseSearch::fulltextDecode($text); + } + + // FIXME: Document me. + private static function makeKeywordsArray($string) + { + /* Mark up quoted strings with filler characters (no white space). */ + $string = DatabaseSearch::markUpQuotes($string); + + /* Split keywords into an array by "words" and fix quotes. */ + $keywords = explode(' ', $string); + $keywords = array_map( + array('DatabaseSearch', 'unMarkUpQuotes'), $keywords + ); + + /* Escape special regex characters in keys, and filter out boolean words. */ + foreach ($keywords as $index => $keyword) + { + $keywords[$index] = str_replace( + array('(', ')'), '', $keywords[$index] + ); + + if (strtoupper($keyword) == 'AND' || + strtoupper($keyword) == 'OR' || + strtoupper($keyword) == 'NOT') + { + unset($keywords[$index]); + continue; + } + } + + return array_merge($keywords); + } +} + + +/** + * Candidates Search Library + * @package CATS + * @subpackage Library + */ +class SearchCandidates +{ + private $_db; + private $_siteID; + protected $_userID = -1; + + + public function __construct($siteID) + { + $this->_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + //FIXME: Library code Session dependencies suck. + $this->_userID = $_SESSION['CATS']->getUserID(); + } + + public function get_attachment_ids_str($keywordstr) + { + $this->_db = DatabaseConnection::getInstance(); + $this->_siteID = $siteID; + + $result = "0"; + $bypass = false; + + if (ENABLE_SPHINX && strlen($keywordstr)>0) + { + /* Sphinx API likes to throw PHP errors *AND* use it's own error + * handling. + */ + //die("

SPHINX-".$keywordstr."-

"); + + assert_options(ASSERT_WARNING, 0); + + $sphinx = new SphinxClient(); + $sphinx->SetServer(SPHINX_HOST, SPHINX_PORT); + $sphinx->SetWeights(array(0, 100, 0, 0, 50)); + $sphinx->SetMatchMode(SPH_MATCH_PHRASE); + $sphinx->SetLimits(0, 30000); + $sphinx->SetArrayResult(false); + $sphinx->SetSortMode(SPH_SORT_TIME_SEGMENTS, 'date_added'); + + //$sphinx->SetFilter('site_id', array($this->_siteID)); + + $wildCardString = '"'.DatabaseSearch::humanToSphinxBoolean($keywordstr).'"'; + + + $tries = 0; + do + { + /* Wait for one second if this isn't out first attempt. */ + if (++$tries > 1) + { + sleep(1); + } + //$wildCardString = '"php"'; + $results = $sphinx->Query($wildCardString, SPHINX_INDEX); + //print_r($results); + //die("-"); + + $errorMessage = $sphinx->GetLastError(); + } + while ( + $results === false && + strpos($errorMessage, 'server maxed out, retry') !== false && + $tries <= 5 + ); + //echo("

".SPHINX_HOST.", ".SPHINX_PORT."

"); + //die($errorMessage); + //die("1) --->[".($results===false)."]"); + + /* Throw a fatal error if Sphinx errors occurred. */ + if ($results === false) + { + $this->fatal('Sphinx Error: ' . ucfirst($errorMessage) . '.'); + } + + /* Throw a fatal error (for now) if Sphinx warnings occurred. */ + $lastWarning = $sphinx->GetLastWarning(); + if (!empty($lastWarning)) + { + // FIXME: Just display a warning, and notify dev team. + $this->fatal('Sphinx Warning: ' . ucfirst($lastWarning) . '.'); + } + + /* Show warnings for assert()s again. */ + assert_options(ASSERT_WARNING, 1); + + //$wildCardString = $wildCardString_orig; + if (empty($results['matches'])) + { + //empty, do nothing + //echo "

(no results)".print_r($results, true)."

"; + } + else + { + //die(print_r($results['matches'])); + $attachmentIDs = implode(',', array_keys($results['matches'])); + $result = 'attachment.attachment_id IN(' . $attachmentIDs . ')'; + } + + } + return $result; + } + + public function byState($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = strtolower('%' . str_replace('*', '%', $wildCardString) . '%'); + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + ( + LOWER(candidate.state) LIKE %s + ) + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $this->_siteID, + $sortBy, + $sortDirection + ); + return $this->_db->getAllAssoc($sql); + } + + public function get_inclusive_zipcodes($distance, $distance_unit, $zip_code) { + $sql = sprintf( + "SELECT + latitude, longitude + FROM + geoip_details + WHERE + LOWER(geoip_details.postalcode) = '%s'", + strtolower($zip_code) + ); + + + $rs = $this->_db->getAllAssoc($sql); + + if($rs) { + foreach ($rs as $rowIndex => $row) { + $latitude = $row["latitude"]; + $longitude = $row["longitude"]; + switch (strtolower($distance_unit)) { + case 'miles': /*** miles ***/ + $unit = 3963; + break; + case 'nauticalmiles': /*** nautical miles ***/ + $unit = 3444; + break; + case 'kilometers': + default: /*** kilometers ***/ + $unit = 6371; + } + $sql_zip = "SELECT postalcode, + (".$unit." * ACOS( COS( RADIANS(".$latitude.") ) * COS( RADIANS( latitude ) ) * COS( RADIANS( longitude ) - RADIANS(".$longitude.") ) + SIN( RADIANS(".$latitude.") ) * SIN( RADIANS( latitude ) ) ) ) AS distance + FROM geoip_details + HAVING distance < ".$distance.""; + $rs_zips = $this->_db->getAllAssoc($sql_zip); + return $rs_zips; + break; + } + } + return false; + } + + function get_countries() { + /* + $sql = sprintf( + "SELECT + abbrev as value, + country as text + FROM + geoip_countries + ORDER BY + country"); + return $this->_db->getAllAssoc($sql); + */ + $result = array( + "1" => array("value" => "US", "text" => "United States"), + "0" => array("value" => "CA", "text" => "Canada") + ); + return $result; + } + + function get_zip_codes($country, $state, $city) { + $sql = sprintf( + "SELECT DISTINCT + postalcode as value, + UPPER(postalcode) as text + FROM + geoip_details + WHERE + country = '%s' + AND + region = '%s' + AND + city = '%s' + ORDER BY + postalcode", + $country, + $state, + $city); + return $this->_db->getAllAssoc($sql); + } + + function get_cities($country, $state) { + $sql = sprintf( + "SELECT DISTINCT + city as value, + city as text + FROM + geoip_details + WHERE + country = '%s' + AND + region = '%s' + ORDER BY + city", + $country, + $state); + return $this->_db->getAllAssoc($sql); + } + + function get_states($country) { + $sql = sprintf( + "SELECT DISTINCT + abbrev as value, + state as text + FROM + geoip_states + WHERE + country = '%s' + ORDER BY state", + $country); + return $this->_db->getAllAssoc($sql); + } + + public function byMultiple($wildCardString, $sortBy, $sortDirection, $return_sql = false) + { + //$wildCardString = strtolower('%' . str_replace('*', '%', $wildCardString) . '%'); + //$wildCardString = $this->_db->makeQueryString($wildCardString); + $wildCardString = str_replace('%', '%%', $wildCardString); + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + ( ".$wildCardString." ) + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + ORDER BY + %s %s + LIMIT 1000", + $this->_siteID, + $sortBy, + $sortDirection + ); + //die($sql); + if($return_sql == true) + return $sql; + else + return $this->_db->getAllAssoc($sql); + } + + public function byCity($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = strtolower('%' . str_replace('*', '%', $wildCardString) . '%'); + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + ( + LOWER(candidate.city) LIKE %s + ) + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $this->_siteID, + $sortBy, + $sortDirection + ); + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all candidates with full names matching $wildCardString. + * + * @param string wildcard match string + * @return array candidates data + */ + public function byFullName($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + ( + CONCAT(candidate.first_name, ' ', candidate.last_name) LIKE %s + OR CONCAT(candidate.last_name, ' ', candidate.first_name) LIKE %s + OR CONCAT(candidate.last_name, ', ', candidate.first_name) LIKE %s + ) + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $wildCardString, + $wildCardString, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all candidates with key skills matching $wildCardString. + * + * @param string wildcard match string + * @return array candidates data + */ + public function byKeySkills($wildCardString, $sortBy, $sortDirection) + { + $WHERE = DatabaseSearch::makeBooleanSQLWhere( + $wildCardString, $this->_db, 'candidate.key_skills' + ); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + %s + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + AND + candidate.is_active = 1 + ORDER BY + %s %s", + $WHERE, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all candidates with E-Mail addresses matching $wildCardString. + * + * @param string wildcard match string + * @return array candidates data + */ + public function byEmail($wildCardString, $sortBy = 'firstName', $sortDirection = 'ASC') + { + $wildCardString = str_replace('*', '%', $wildCardString); + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + candidate.email1 LIKE %s + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all candidates with phone numbers matching $wildCardString. + * + * @param string wildcard match string + * @return array candidates data + */ + public function byPhone($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = str_replace( + array('.', '-', '(', ')'), + '', + $wildCardString + ); + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + ( + REPLACE( + REPLACE( + REPLACE( + REPLACE(candidate.phone_home, '-', ''), + '.', ''), + ')', ''), + '(', '') LIKE %s + OR REPLACE( + REPLACE( + REPLACE( + REPLACE(candidate.phone_cell, '-', ''), + '.', ''), + ')', ''), + '(', '') LIKE %s + ) + AND + candidate.is_admin_hidden = 0 + AND + candidate.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $wildCardString, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } +} + + +/** + * Companies Search Library + * @package CATS + * @subpackage Library + */ +class SearchCompanies +{ + private $_db; + private $_siteID; + protected $_userID = -1; + + + public function __construct($siteID) + { + $this->_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + //FIXME: Library code Session dependencies suck. + $this->_userID = $_SESSION['CATS']->getUserID(); + } + + public function get_inclusive_zipcodes($distance, $distance_unit, $zip_code) { + $sql = sprintf( + "SELECT + latitude, longitude + FROM + geoip_details + WHERE + LOWER(geoip_details.postalcode) = '%s'", + strtolower($zip_code) + ); + + + $rs = $this->_db->getAllAssoc($sql); + + if($rs) { + foreach ($rs as $rowIndex => $row) { + $latitude = $row["latitude"]; + $longitude = $row["longitude"]; + switch (strtolower($distance_unit)) { + case 'miles': /*** miles ***/ + $unit = 3963; + break; + case 'nauticalmiles': /*** nautical miles ***/ + $unit = 3444; + break; + case 'kilometers': + default: /*** kilometers ***/ + $unit = 6371; + } + $sql_zip = "SELECT postalcode, + (".$unit." * ACOS( COS( RADIANS(".$latitude.") ) * COS( RADIANS( latitude ) ) * COS( RADIANS( longitude ) - RADIANS(".$longitude.") ) + SIN( RADIANS(".$latitude.") ) * SIN( RADIANS( latitude ) ) ) ) AS distance + FROM geoip_details + HAVING distance < ".$distance.""; + $rs_zips = $this->_db->getAllAssoc($sql_zip); + return $rs_zips; + break; + } + } + return false; + } + + /** + * Returns all companies with names matching $wildCardString. + * + * @param string wildcard match string + * @return array companies data + */ + public function byMultiple($wildCardString, $sortBy, $sortDirection) + { + //$wildCardString = str_replace('*', '%', $wildCardString) . '%'; + //$wildCardString = $this->_db->makeQueryString($wildCardString); + $wildCardString = str_replace('%', '%%', $wildCardString); + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS name, + company.city AS city, + company.state AS state, + company.phone1 AS phone1, + company.url AS url, + company.key_technologies AS keyTechnologies, + company.is_hot AS isHot, + DATE_FORMAT( + company.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + company.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName + FROM + company + LEFT JOIN user AS owner_user + ON company.owner = owner_user.user_id + WHERE + (".$wildCardString.") + AND + company.site_id = %s + ORDER BY + %s %s + LIMIT 3000", + $this->_siteID, + $sortBy, + $sortDirection + ); + //die($sql); + + return $this->_db->getAllAssoc($sql); + } + + public function byName($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS name, + company.city AS city, + company.state AS state, + company.phone1 AS phone1, + company.url AS url, + company.key_technologies AS keyTechnologies, + company.is_hot AS isHot, + DATE_FORMAT( + company.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + company.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName + FROM + company + LEFT JOIN user AS owner_user + ON company.owner = owner_user.user_id + WHERE + company.name LIKE %s + AND + company.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all companies with key technologies matching $wildCardString. + * + * @param string wildcard match string + * @return array candidates data + */ + public function byKeyTechnologies($wildCardString) + { + $WHERE = DatabaseSearch::makeBooleanSQLWhere( + $wildCardString, $this->_db, 'company.key_technologies' + ); + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS name, + company.city AS city, + company.state AS state, + company.phone1 AS phone1, + company.url AS url, + company.key_technologies AS keyTechnologies, + company.is_hot AS isHot, + DATE_FORMAT( + company.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + company.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName + FROM + company + LEFT JOIN user AS owner_user + ON company.owner = owner_user.user_id + WHERE + %s + AND + company.site_id = %s + ORDER BY + company.name ASC", + $WHERE, + $this->_siteID + ); + + return $this->_db->getAllAssoc($sql); + } +} + +/** + * Job Orders Search Library + * @package CATS + * @subpackage Library + */ +class SearchJobOrders +{ + private $_db; + private $_siteID; + protected $_userID = -1; + + + public function __construct($siteID) + { + $this->_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + //FIXME: Library code Session dependencies suck. + $this->_userID = $_SESSION['CATS']->getUserID(); + } + + + /** + * Returns all job orders with titles matching $wildCardString. If + * activeOnly is true, only Active/OnHold/Full job orders will be shown. + * + * @param string wildcard match string + * @param boolean return active job orders only + * @return array job orders data + */ + public function byTitle($wildCardString, $sortBy, $sortDirection, + $activeOnly) + { + if ($activeOnly) + { + //FIXME: Remove session dependancy. + if ($_SESSION['CATS']->isFree()) + { + $activeCriterion = "AND joborder.status = 'Active'"; + } + else + { + $activeCriterion = "AND (joborder.status IN ('Active', 'OnHold', 'Full'))"; + } + } + else + { + $activeCriterion = ""; + } + + $WHERE = DatabaseSearch::makeBooleanSQLWhere( + $wildCardString, $this->_db, 'joborder.title' + ); + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS companyName, + joborder.joborder_id AS jobOrderID, + joborder.title AS title, + joborder.type AS type, + joborder.is_hot AS isHot, + joborder.duration AS duration, + joborder.rate_max AS maxRate, + joborder.salary AS salary, + joborder.status AS status, + recruiter_user.first_name AS recruiterFirstName, + recruiter_user.last_name AS recruiterLastName, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + joborder.start_date, '%%m-%%d-%%y' + ) AS startDate, + DATE_FORMAT( + joborder.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + joborder.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + company + LEFT JOIN joborder + ON company.company_id = joborder.company_id + LEFT JOIN user AS recruiter_user + ON joborder.recruiter = recruiter_user.user_id + LEFT JOIN user AS owner_user + ON joborder.owner = owner_user.user_id + WHERE + %s + %s + AND + joborder.is_admin_hidden = 0 + AND + joborder.site_id = %s + ORDER BY + %s %s", + $WHERE, + $activeCriterion, + $this->_siteID, + $sortBy, + $sortDirection + ); + + if (!eval(Hooks::get('JO_SEARCH_SQL'))) return; + if (!eval(Hooks::get('JO_SEARCH_BY_TITLE'))) return; + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all job orders with company names matching $wildCardString. If + * activeOnly is true, only Active/OnHold/Full job orders will be shown. + * + * @param string wildcard match string + * @param boolean return active job orders only + * @return array job orders data + */ + public function byCompanyName($wildCardString, $sortBy, $sortDirection, $activeOnly) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + if ($activeOnly) + { + //FIXME: Remove session dependancy. + if ($_SESSION['CATS']->isFree()) + { + $activeCriterion = "AND joborder.status = 'Active'"; + } + else + { + $activeCriterion = "AND (joborder.status IN ('Active', 'OnHold', 'Full'))"; + } + } + else + { + $activeCriterion = ""; + } + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS companyName, + joborder.joborder_id AS jobOrderID, + joborder.title AS title, + joborder.type AS type, + joborder.is_hot AS isHot, + joborder.duration AS duration, + joborder.rate_max AS maxRate, + joborder.salary AS salary, + joborder.status AS status, + recruiter_user.first_name AS recruiterFirstName, + recruiter_user.last_name AS recruiterLastName, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + joborder.start_date, '%%m-%%d-%%y' + ) AS startDate, + DATE_FORMAT( + joborder.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + joborder.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + company + LEFT JOIN joborder + ON company.company_id = joborder.company_id + LEFT JOIN user AS recruiter_user + ON joborder.recruiter = recruiter_user.user_id + LEFT JOIN user AS owner_user + ON joborder.owner = owner_user.user_id + WHERE + company.name LIKE %s + %s + AND + joborder.is_admin_hidden = 0 + AND + joborder.site_id = %s + AND + company.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $activeCriterion, + $this->_siteID, + $this->_siteID, + $sortBy, + $sortDirection + ); + + if (!eval(Hooks::get('JO_SEARCH_SQL'))) return; + if (!eval(Hooks::get('JO_SEARCH_BY_CLIENT_NAME'))) return; + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all recently modified job orders. If activeOnly is true, + * only Active/OnHold/Full job orders will be shown. + * + * @param boolean return active job orders only + * @return array job orders data + */ + public function recentlyModified($sortDirection, $activeOnly, $limit) + { + if ($activeOnly) + { + //FIXME: Remove session dependancy. + if ($_SESSION['CATS']->isFree()) + { + $activeCriterion = "AND joborder.status = 'Active'"; + } + else + { + $activeCriterion = "AND (joborder.status IN ('Active', 'OnHold', 'Full'))"; + } + } + else + { + $activeCriterion = ""; + } + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS companyName, + joborder.joborder_id AS jobOrderID, + joborder.title AS title, + joborder.type AS type, + joborder.is_hot AS isHot, + joborder.duration AS duration, + joborder.rate_max AS maxRate, + joborder.salary AS salary, + joborder.status AS status, + recruiter_user.first_name AS recruiterFirstName, + recruiter_user.last_name AS recruiterLastName, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + joborder.start_date, '%%m-%%d-%%y' + ) AS startDate, + DATE_FORMAT( + joborder.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + joborder.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + joborder.date_modified AS dateModifiedSort + FROM + company + LEFT JOIN joborder + ON company.company_id = joborder.company_id + LEFT JOIN user AS recruiter_user + ON joborder.recruiter = recruiter_user.user_id + LEFT JOIN user AS owner_user + ON joborder.owner = owner_user.user_id + WHERE + joborder.site_id = %s + %s + AND + company.site_id = %s + AND + joborder.is_admin_hidden = 0 + ORDER BY + dateModifiedSort %s + LIMIT 0, %s", + $this->_siteID, + $activeCriterion, + $this->_siteID, + $sortDirection, + $limit + ); + + if (!eval(Hooks::get('JO_SEARCH_SQL'))) return; + + return $this->_db->getAllAssoc($sql); + } +} + + +/** + * Contacts Search Library + * @package CATS + * @subpackage Library + */ +class ContactsSearch +{ + private $_db; + private $_siteID; + protected $_userID = -1; + + + public function __construct($siteID) + { + $this->_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + //FIXME: Library code Session dependencies suck. + $this->_userID = $_SESSION['CATS']->getUserID(); + } + + + /** + * Returns all contacts with full names matching $wildCardString. + * + * @param string wildcard match string + * @return array contacts data + */ + public function byFullName($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + contact.contact_id AS contactID, + contact.company_id AS companyID, + contact.last_name AS lastName, + contact.first_name AS firstName, + contact.title AS title, + contact.phone_work AS phoneWork, + contact.phone_cell AS phoneCell, + contact.phone_other AS phoneOther, + contact.email1 AS email1, + contact.email2 AS email2, + contact.is_hot AS isHotContact, + contact.left_company AS leftCompany, + DATE_FORMAT( + contact.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + contact.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + company.name AS companyName, + company.is_hot AS isHotCompany + FROM + contact + LEFT JOIN company + ON contact.company_id = company.company_id + LEFT JOIN user AS owner_user + ON contact.owner = owner_user.user_id + WHERE + ( + CONCAT(contact.first_name, ' ', contact.last_name) LIKE %s + OR CONCAT(contact.last_name, ' ', contact.first_name) LIKE %s + OR CONCAT(contact.last_name, ', ', contact.first_name) LIKE %s + ) + AND + contact.site_id = %s + AND + company.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $wildCardString, + $wildCardString, + $this->_siteID, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all contacts with company names matching $wildCardString. + * + * @param string wildcard match string + * @return array contacts data + */ + public function byCompanyName($wildCardString, $sortBy, + $sortDirection) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + contact.contact_id AS contactID, + contact.company_id AS companyID, + contact.last_name AS lastName, + contact.first_name AS firstName, + contact.title AS title, + contact.phone_work AS phoneWork, + contact.phone_cell AS phoneCell, + contact.phone_other AS phoneOther, + contact.email1 AS email1, + contact.email2 AS email2, + contact.is_hot AS isHotContact, + contact.left_company AS leftCompany, + DATE_FORMAT( + contact.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + contact.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + company.name AS companyName, + company.is_hot AS isHotCompany + FROM + contact + LEFT JOIN company + ON contact.company_id = company.company_id + LEFT JOIN user AS owner_user + ON contact.owner = owner_user.user_id + WHERE + company.name LIKE %s + AND + contact.site_id = %s + AND + company.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $this->_siteID, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns all contacts with titles matching $wildCardString. + * + * @param string wildcard match string + * @return array contacts data + */ + public function byTitle($wildCardString, $sortBy, $sortDirection) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + contact.contact_id AS contactID, + contact.company_id AS companyID, + contact.last_name AS lastName, + contact.first_name AS firstName, + contact.title AS title, + contact.phone_work AS phoneWork, + contact.phone_cell AS phoneCell, + contact.phone_other AS phoneOther, + contact.email1 AS email1, + contact.email2 AS email2, + contact.is_hot AS isHotContact, + contact.left_company AS leftCompany, + DATE_FORMAT( + contact.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + contact.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + company.name AS companyName, + contact.is_hot AS isHotCompany + FROM + contact + LEFT JOIN company + ON contact.company_id = company.company_id + LEFT JOIN user AS owner_user + ON contact.owner = owner_user.user_id + WHERE + contact.title LIKE %s + AND + contact.site_id = %s + AND + company.site_id = %s + ORDER BY + %s %s", + $wildCardString, + $this->_siteID, + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } +} + + +/** + * Quick Search Library + * @package CATS + * @subpackage Library + */ +class QuickSearch +{ + private $_db; + private $_siteID; + protected $_userID = -1; + + + public function __construct($siteID) + { + $this->_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + //FIXME: Library code Session dependencies suck. + $this->_userID = $_SESSION['CATS']->getUserID(); + } + + + /** + * Support function for Quick Search code. Searches all relevant fields for + * $wildCardString. + * + * @param string wildcard match string + * @return array candidates data + */ + public function candidates($wildCardString) + { + $wildCardStringRaw = $wildCardString; + + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $wildCardStringWholeCaseInsensitive = $this->_db->makeQueryString(strtolower('%'.str_replace('*', '%',$wildCardStringRaw).'%')); + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.key_skills AS keySkills, + candidate.email1 AS email1, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + ( + LOWER(candidate.city) LIKE %s + OR LOWER(candidate.state) LIKE %s + OR LOWER(candidate.skill_texts) LIKE %s + OR CONCAT(candidate.first_name, ' ', candidate.last_name) LIKE %s + OR CONCAT(candidate.last_name, ' ', candidate.first_name) LIKE %s + OR CONCAT(candidate.last_name, ', ', candidate.first_name) LIKE %s + OR candidate.email1 LIKE %s + OR REPLACE( + REPLACE( + REPLACE( + REPLACE(candidate.phone_home, '-', ''), + '.', ''), + ')', ''), + '(', '') LIKE %s + OR REPLACE( + REPLACE( + REPLACE( + REPLACE(candidate.phone_cell, '-', ''), + '.', ''), + ')', ''), + '(', '') LIKE %s + ) + AND + candidate.site_id = %s + AND + candidate.is_admin_hidden = 0 + ORDER BY + candidate.date_modified DESC, + candidate.first_name ASC, + candidate.last_name ASC", + $wildCardStringWholeCaseInsensitive, + $wildCardStringWholeCaseInsensitive, + $wildCardStringWholeCaseInsensitive, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $this->_siteID + ); + //die($sql); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Support function for Quick Search code. Searches all relevant fields for + * $wildCardString. + * + * @param string wildcard match string + * @return array companies data + */ + public function companies($wildCardString) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS name, + company.city AS city, + company.state AS state, + company.phone1 AS phone1, + company.url AS url, + company.key_technologies AS keyTechnologies, + company.is_hot AS isHot, + DATE_FORMAT( + company.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + company.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName + FROM + company + LEFT JOIN user AS owner_user + ON company.owner = owner_user.user_id + WHERE + ( + company.name LIKE %s + OR company.phone1 LIKE %s + OR company.phone2 LIKE %s + OR company.url LIKE %s + ) + AND + company.site_id = %s + ORDER BY + company.name ASC", + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $this->_siteID + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Support function for Quick Search code. Searches all relevant fields for + * $wildCardString. + * + * @param string wildcard match string + * @return array contacts data + */ + public function contacts($wildCardString) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + contact.contact_id AS contactID, + contact.company_id AS companyID, + contact.last_name AS lastName, + contact.first_name AS firstName, + contact.title AS title, + contact.phone_work AS phoneWork, + contact.phone_cell AS phoneCell, + contact.phone_other AS phoneOther, + contact.email1 AS email1, + contact.email2 AS email2, + contact.is_hot AS isHotContact, + contact.left_company AS leftCompany, + DATE_FORMAT( + contact.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + contact.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + company.name AS companyName, + company.is_hot AS isHotCompany + FROM + contact + LEFT JOIN company + ON contact.company_id = company.company_id + LEFT JOIN user AS owner_user + ON contact.owner = owner_user.user_id + WHERE + ( + CONCAT(contact.first_name, ' ', contact.last_name) LIKE %s + OR CONCAT(contact.last_name, ' ', contact.first_name) LIKE %s + OR CONCAT(contact.last_name, ', ', contact.first_name) LIKE %s + OR contact.phone_work LIKE %s + OR company.name LIKE %s + OR contact.email1 LIKE %s + OR contact.email2 LIKE %s + OR REPLACE( + REPLACE( + REPLACE( + REPLACE(contact.phone_work, '-', ''), + '.', ''), + ')', ''), + '(', '') LIKE %s + OR REPLACE( + REPLACE( + REPLACE( + REPLACE(contact.phone_cell, '-', ''), + '.', ''), + ')', ''), + '(', '') LIKE %s + ) + AND + contact.site_id = %s + AND + company.site_id = %s + ORDER BY + name ASC", + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $wildCardString, + $this->_siteID, + $this->_siteID + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Support function for Quick Search code. Searches all relevant fields for + * $wildCardString. + * + * @param string wildcard match string + * @return array job orders data + */ + public function jobOrders($wildCardString) + { + $wildCardString = str_replace('*', '%', $wildCardString) . '%'; + $wildCardString = $this->_db->makeQueryString($wildCardString); + + $sql = sprintf( + "SELECT + company.company_id AS companyID, + company.name AS companyName, + joborder.joborder_id AS jobOrderID, + joborder.title AS title, + joborder.type AS type, + joborder.is_hot AS isHot, + joborder.duration AS duration, + joborder.rate_max AS maxRate, + joborder.salary AS salary, + joborder.status AS status, + joborder.city AS city, + joborder.state AS state, + recruiter_user.first_name AS recruiterFirstName, + recruiter_user.last_name AS recruiterLastName, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + DATE_FORMAT( + joborder.start_date, '%%m-%%d-%%y' + ) AS startDate, + DATE_FORMAT( + joborder.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + joborder.date_modified, '%%m-%%d-%%y' + ) AS dateModified + FROM + joborder + LEFT JOIN company + ON joborder.company_id = company.company_id + LEFT JOIN user AS recruiter_user + ON joborder.recruiter = recruiter_user.user_id + LEFT JOIN user AS owner_user + ON joborder.owner = owner_user.user_id + WHERE + ( + company.name LIKE %s + OR joborder.title LIKE %s + ) + AND + joborder.is_admin_hidden = 0 + AND + joborder.site_id = %s + AND + company.site_id = %s + ORDER BY + name ASC", + $wildCardString, + $wildCardString, + $this->_siteID, + $this->_siteID + ); + + if (!eval(Hooks::get('JO_SEARCH_SQL'))) return; + if (!eval(Hooks::get('JO_SEARCH_BY_EVERYTHING'))) return; + + return $this->_db->getAllAssoc($sql); + } +} + +/** + * Saved Searches Library + * @package CATS + * @subpackage Library + */ +class SavedSearches +{ + private $_db; + private $_siteID; + protected $_userID = -1; + + + public function __construct($siteID) + { + $this->_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + //FIXME: Library code Session dependencies suck. + $this->_userID = $_SESSION['CATS']->getUserID(); + } + + + /** + * Removes a saved search entry. + * + * @param integer search ID + * @return void + */ + public function remove($searchID) + { + $sql = sprintf( + "DELETE FROM + saved_search + WHERE + search_id = %s + AND + user_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($searchID), + $this->_userID, + $this->_siteID + ); + $this->_db->query($sql); + } + + /** + * Promotes a recent search to a saved search. + * + * @param integer search ID + * @return boolean True if successful; false otherwise. + */ + public function save($searchID) + { + $sql = sprintf( + "UPDATE + saved_search + SET + is_custom = 1 + WHERE + search_id = %s + AND + user_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($searchID), + $this->_userID, + $this->_siteID + ); + + return (boolean) $this->_db->query($sql); + } + + //FIXME: Document me. + public function removeRecent($dataItemType, $text) + { + $sql = sprintf( + "DELETE FROM + saved_search + WHERE + data_item_text = %s + AND + data_item_type = %s + AND + user_id = %s + AND + is_custom = 0 + AND + site_id = %s", + $this->_db->makeQueryString($text), + $this->_db->makeQueryInteger($dataItemType), + $this->_userID, + $this->_siteID + ); + $this->_db->query($sql); + } + + //FIXME: Document me. + public function add($dataItemType, $text, $url, $isCustom) + { + /* If this item is already in the saved search list, remove it. */ + $this->removeRecent($dataItemType, $text); + + $sql = sprintf( + "INSERT INTO saved_search ( + site_id, + user_id, + data_item_type, + data_item_text, + url, + is_custom, + date_created + ) + VALUES ( + %s, + %s, + %s, + %s, + %s, + %s, + NOW() + )", + $this->_siteID, + $this->_userID, + $this->_db->makeQueryInteger($dataItemType), + $this->_db->makeQueryString($text), + $this->_db->makeQueryString($url), + ($isCustom ? 1 : 0) + ); + $this->_db->query($sql); + + $this->prune(); + } + + //FIXME: Document me. + public function get($dataItemType) + { + $sql = sprintf( + "SELECT + search_id AS searchID, + data_item_text AS dataItemText, + url AS URL, + is_custom AS isCustom + FROM + saved_search + WHERE + site_id = %s + AND + user_id = %s + AND + data_item_type = %s + ORDER BY + search_id DESC", + $this->_siteID, + $this->_userID, + $this->_db->makeQueryInteger($dataItemType) + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Removes old saved search entries for a user. + * + * @return void + */ + private function prune() + { + $sql = sprintf( + "SELECT + COUNT(*) AS count + FROM + saved_search + WHERE + site_id = %s + AND + user_id = %s + AND + is_custom = 0", + $this->_siteID, + $this->_userID + ); + $rs = $this->_db->getAssoc($sql); + + $count = $rs['count']; + + // FIXME: Remove multiple entries at onceif we're more than one over? + while ($count > RECENT_SEARCH_MAX_ITEMS) + { + /* Remove the least recent entry. */ + $sql = sprintf( + "SELECT + search_id AS searchID + FROM + saved_search + WHERE + site_id = %s + AND + user_id = %s + AND + is_custom = 0 + ORDER BY + search_id + ASC LIMIT 1", + $this->_siteID, + $this->_userID + ); + $rs = $this->_db->getAssoc($sql); + + $sql = sprintf( + "DELETE FROM + saved_search + WHERE + search_id = %s", + $rs['searchID'] + ); + $this->_db->query($sql); + + --$count; + } + } +} + + +/** + * Search by Resume Pager + * @package CATS + * @subpackage Library + */ +class SearchByResumePager extends Pager +{ + private $_siteID; + private $_db; + private $_WHERE; + + + public function __construct($rowsPerPage, $currentPage, $siteID, + $wildCardString, $sortBy, $sortDirection, $single_resume_text) + { + $this->_db = DatabaseConnection::getInstance(); + $this->_siteID = $siteID; + + $this->_sortByFields = array( + 'firstName', + 'lastName', + 'city', + 'state', + 'dateModifiedSort', + 'dateCreatedSort', + 'ownerSort' + ); + + if (ENABLE_SPHINX && strlen($single_resume_text)>0) + { + /* Sphinx API likes to throw PHP errors *AND* use it's own error + * handling. + */ + assert_options(ASSERT_WARNING, 0); + + $sphinx = new SphinxClient(); + $sphinx->SetServer(SPHINX_HOST, SPHINX_PORT); + $sphinx->SetWeights(array(0, 100, 0, 0, 50)); + $sphinx->SetMatchMode(SPH_MATCH_ANY); + $sphinx->SetLimits(0, 30000); + $sphinx->SetArrayResult(true); + $sphinx->SetSortMode(SPH_SORT_TIME_SEGMENTS, 'date_added'); + + // FIXME: This can be sped up a bit by actually grouping ranges of + // site IDs into their own index's. Maybe every 500 or so at + // least on the Hosted system. + $sphinx->SetFilter('site_id', array($this->_siteID)); + + /* Create the Sphinx query string. */ + //$wildCardString = DatabaseSearch::humanToSphinxBoolean($wildCardString); + //die($wildCardString); + + $wildCardString_orig = $wildCardString; + $wildCardString = '"'.DatabaseSearch::humanToSphinxBoolean($single_resume_text).'"'; + + //die($wildCardString); + + /* Execute the Sphinx query. Sphinx can ask us to retry if its + * maxed out. Retry up to 5 times. + */ + $tries = 0; + do + { + /* Wait for one second if this isn't out first attempt. */ + if (++$tries > 1) + { + sleep(1); + } + + //$wildCardString = '"php"'; + $results = $sphinx->Query($wildCardString, SPHINX_INDEX); + //print_r($results); + //die("-"); + + $errorMessage = $sphinx->GetLastError(); + } + while ( + $results === false && + strpos($errorMessage, 'server maxed out, retry') !== false && + $tries <= 5 + ); + + /* Throw a fatal error if Sphinx errors occurred. */ + if ($results === false) + { + $this->fatal('Sphinx Error: ' . ucfirst($errorMessage) . '.'); + } + + /* Throw a fatal error (for now) if Sphinx warnings occurred. */ + $lastWarning = $sphinx->GetLastWarning(); + if (!empty($lastWarning)) + { + // FIXME: Just display a warning, and notify dev team. + $this->fatal('Sphinx Warning: ' . ucfirst($lastWarning) . '.'); + } + + /* Show warnings for assert()s again. */ + assert_options(ASSERT_WARNING, 1); + + //print_r($results); + //die("end"); + + $wildCardString = $wildCardString_orig; + if (empty($results['matches'])) + { + + if(strlen($wildCardString)>0) + $this->_WHERE = "(".$wildCardString.")"; + else { + $this->_WHERE = '0'; + $wildCardString = "0"; + } + + //echo("

passA - ".$this->_WHERE."

"); + } + else + { + + $attachmentIDs = implode(',', array_keys($results['matches'])); + $this->_WHERE = 'attachment.attachment_id IN(' . $attachmentIDs . ')'; + if(strlen($wildCardString)>0) + $this->_WHERE .= " AND (".$wildCardString.")"; + else + $wildCardString = "0"; + + //echo("

passB - ".$this->_WHERE."

"); + } + + } + else + { + /* + $wildCardString = str_replace("(", "", $wildCardString); + $wildCardString = str_replace(")", "", $wildCardString); + + $this->_WHERE = DatabaseSearch::makeBooleanSQLWhere( + DatabaseSearch::fulltextEncode($wildCardString), + $this->_db, + 'attachment.text' + ); + */ + $this->_WHERE = "(".$wildCardString.")"; + } + + //die($this->_WHERE); + + /* How many companies do we have? */ + $sql = sprintf( + "SELECT + COUNT(*) AS count + FROM + attachment + LEFT JOIN candidate + ON attachment.data_item_id = candidate.candidate_id + AND attachment.data_item_type = %s + AND attachment.site_id = candidate.site_id + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + resume = 1 + AND + %s + AND + (ISNULL(candidate.is_admin_hidden) OR (candidate.is_admin_hidden = 0)) + AND + (ISNULL(candidate.is_active) OR (candidate.is_active = 1)) + AND + attachment.site_id = %s", + DATA_ITEM_CANDIDATE, + $this->_WHERE, + $this->_siteID + ); + + //die($sql); + $rs = $this->_db->getAssoc($sql); + + /* Pass "Search By Resume"-specific parameters to Pager constructor. */ + parent::__construct($rs['count'], $rowsPerPage, $currentPage); + } + + + + //FIXME: Document me. + public function getPage() + { + $sql = sprintf( + "SELECT + attachment.attachment_id AS attachmentID, + attachment.data_item_id AS candidateID, + attachment.title AS title, + attachment.text AS text, + candidate.first_name AS firstName, + candidate.last_name AS lastName, + candidate.city AS city, + candidate.state AS state, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + candidate.date_created AS dateCreatedSort, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + candidate.date_modified AS dateModifiedSort, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName, + CONCAT(owner_user.last_name, owner_user.first_name) AS ownerSort + FROM + attachment + LEFT JOIN candidate + ON attachment.data_item_id = candidate.candidate_id + AND attachment.site_id = candidate.site_id + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + WHERE + resume = 1 + AND + %s + AND + (attachment.data_item_type = %s OR attachment.data_item_type = %s) + AND + attachment.site_id = %s + AND + (ISNULL(candidate.is_admin_hidden) OR (candidate.is_admin_hidden = 0)) + AND + (ISNULL(candidate.is_active) OR (candidate.is_active = 1)) + ORDER BY + %s %s + LIMIT %s, %s", + + $this->_WHERE, + DATA_ITEM_CANDIDATE, + DATA_ITEM_BULKRESUME, + $this->_siteID, + $this->_sortBy, + $this->_sortDirection, + $this->_thisPageStartRow, + $this->_rowsPerPage + ); + //die($sql); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Print a fatal error and die. + * + * @param string error message + * @return void + */ + protected function fatal($error) + { + $template = new Template(); + $template->assign('errorMessage', $error); + $template->display('./Error.tpl'); + die(); + } +} + + +/** + * Search Results Pager + * @package CATS + * @subpackage Library + */ +class SearchPager extends Pager +{ + private $_siteID; + private $_db; + private $_rs; + + + public function __construct($rowsPerPage, $currentPage, $siteID) + { + $this->_sortByFields = array( + 'firstName', + 'lastName', + 'city', + 'state', + 'dateModified', + 'dateCreated', + 'owner', + 'phone1', + 'companyName', + 'title', + 'owner_user', + 'owner_user.last_name', + 'type', + 'status', + 'startDate', + 'recruiterLastName', + 'dateCreatedSort', + 'dateModifiedSort', + 'ownerSort' + ); + + /* Pass "Search By Resume"-specific parameters to Pager constructor. */ + parent::__construct(count($this->_rs), $rowsPerPage, $currentPage); + } +} + +?> diff --git a/optional-updates/latest-sphinx-search/config.php b/optional-updates/latest-sphinx-search/config.php new file mode 100644 index 000000000..3391fe599 --- /dev/null +++ b/optional-updates/latest-sphinx-search/config.php @@ -0,0 +1,249 @@ + diff --git a/optional-updates/latest-sphinx-search/sphinx.conf b/optional-updates/latest-sphinx-search/sphinx.conf new file mode 100644 index 000000000..f79f1516d --- /dev/null +++ b/optional-updates/latest-sphinx-search/sphinx.conf @@ -0,0 +1,76 @@ +# +# Minimal Sphinx configuration sample (clean, simple, functional) +# + +source catsdb +{ + type = mysql + + sql_host = localhost + sql_user = cats + sql_pass = yourpasshere + sql_db = cats + sql_port = 3306 # optional, default is 3306 + + sql_query_pre = \ + REPLACE INTO sph_counter SELECT 1, MAX(attachment_id) from attachment + + sql_query = \ + SELECT attachment_id, title, attachment.site_id AS site_id, UNIX_TIMESTAMP(attachment.date_created) AS date_added, text \ + FROM attachment LEFT JOIN candidate ON data_item_id = candidate_id \ + WHERE resume = 1 AND data_item_type IN(100,500) AND text IS NOT NULL AND text != '' \ + AND attachment_id <= (SELECT max_doc_id FROM sph_counter WHERE counter_id = 1) + + sql_attr_uint = site_id + sql_attr_timestamp = date_added + + sql_query_info = SELECT * FROM attachment WHERE attachment_id=$id +} + +source delta : catsdb +{ + sql_query_pre = + sql_query = \ + SELECT attachment_id, title, attachment.site_id AS site_id, UNIX_TIMESTAMP(attachment.date_created) AS date_added, text \ + FROM attachment LEFT JOIN candidate ON data_item_id = candidate_id \ + WHERE resume = 1 AND data_item_type IN(100,500) AND text IS NOT NULL AND text != '' \ + AND attachment_id > (SELECT max_doc_id FROM sph_counter WHERE counter_id = 1) +} + +index cats +{ + source = catsdb + path = /opt/var/data/catsindex + docinfo = extern + min_word_len = 1 + charset_type = utf-8 +} + +index catsdelta : cats +{ + source = delta + path = /opt/var/data/catsdelta +} + +indexer +{ + mem_limit = 32M +} + + +searchd +{ + listen = 9312 + listen = 9306:mysql41 + log = /opt/var/log/searchd.log + query_log = /opt/var/log/query.log + read_timeout = 5 + max_children = 30 + pid_file = /opt/var/log/searchd.pid + max_matches = 1000 + seamless_rotate = 1 + preopen_indexes = 1 + unlink_old = 1 + workers = threads # for RT to work + binlog_path = /opt/var/data +} From 11c74483f350648361bb90549f264a165a35110a Mon Sep 17 00:00:00 2001 From: RussH Date: Thu, 3 Sep 2015 19:35:16 +0100 Subject: [PATCH 3/7] Update README.MD --- optional-updates/latest-sphinx-search/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optional-updates/latest-sphinx-search/README.MD b/optional-updates/latest-sphinx-search/README.MD index f1ae300f1..cb54c0a6f 100644 --- a/optional-updates/latest-sphinx-search/README.MD +++ b/optional-updates/latest-sphinx-search/README.MD @@ -2,7 +2,7 @@ Installation instructions for installing the latest Sphinx Search are here https://github.com/opencats/OpenCATS/wiki/Latest-Sphinx-search Please execute this AFTER a normal OpenCATS installation to update Sphinx. -Note: at some future point exisitng 'install Sphinx' instrucitons will be replaced with this verison in its entirety. +Note: at some future point exisitng 'install Sphinx' instructions will be replaced with this verison in its entirety. The attached sample files in this directory are the sample configuration files that are referred to in the above wiki article. From 799c776205422d896fa5dd25e3bcf3eaa459b678 Mon Sep 17 00:00:00 2001 From: RussH Date: Fri, 4 Sep 2015 16:09:43 +0100 Subject: [PATCH 4/7] Update config.php updated the config.php comment to correctly reference the reference for sendmail in MAIL_MAILER settings. --- config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.php b/config.php index a08e303c2..9101c7ae1 100755 --- a/config.php +++ b/config.php @@ -186,7 +186,7 @@ define('MAIL_MAILER', 1); /* Sendmail Settings. You don't need to worry about this unless MAIL_MAILER - * is set to 1. + * is set to 2. */ define('MAIL_SENDMAIL_PATH', "/usr/sbin/sendmail"); From 13e8f6f3f855d91d5f4cbbd52a4cd55a47dae622 Mon Sep 17 00:00:00 2001 From: RussH Date: Fri, 4 Sep 2015 16:13:04 +0100 Subject: [PATCH 5/7] Update config.php set resfly parsing to 'false' by default. --- config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.php b/config.php index 9101c7ae1..76bfc47e0 100755 --- a/config.php +++ b/config.php @@ -37,7 +37,7 @@ define('DATABASE_NAME', 'cats_dev'); /* Resfly.com Resume Import Services Enabled */ -define('PARSING_ENABLED', true); +define('PARSING_ENABLED', false); /* If you have an SSL compatible server, you can enable SSL for all of CATS. */ define('SSL_ENABLED', false); From d59ec4a46c4d106f522914f09663a5a04755a4a0 Mon Sep 17 00:00:00 2001 From: wjcheers Date: Wed, 23 Sep 2015 14:19:28 +0800 Subject: [PATCH 6/7] Fix bug and add duplicated phone number check. 1. fix bug: candidate work phone filter is not working 2. add function: check phone number is duplicated when add a candidate. 3. fix correct index name: in lib/CATSUtility.php --- ajax/getCandidateIdByPhone.php | 74 ++++++++++++++++++++++++++++++++++ js/candidate.js | 71 ++++++++++++++++++++++++++++++++ lib/CATSUtility.php | 2 +- lib/Candidates.php | 33 ++++++++++++++- modules/candidates/Add.tpl | 22 +++++++--- 5 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 ajax/getCandidateIdByPhone.php diff --git a/ajax/getCandidateIdByPhone.php b/ajax/getCandidateIdByPhone.php new file mode 100644 index 000000000..04418b20b --- /dev/null +++ b/ajax/getCandidateIdByPhone.php @@ -0,0 +1,74 @@ +getSiteID(); + + $phone = $_REQUEST['phone']; + + $candidates = new Candidates($siteID); + + $output = "\n"; + + $candidateID = $candidates->getIDByPhone($phone); + + if ($candidateID == -1) + { + $output .= + " \n" . + " -1\n" . + " \n"; + } + else + { + $candidateRS = $candidates->get($candidateID); + + $output .= + " \n" . + " " . $candidateID . "\n" . + " " . $candidateRS['candidateFullName'] . "\n" . + " \n"; + } + $output .= + "\n"; + + /* Send back the XML data. */ + $interface->outputXMLPage($output); + +?> + + diff --git a/js/candidate.js b/js/candidate.js index 1031555b1..891c2d4d1 100755 --- a/js/candidate.js +++ b/js/candidate.js @@ -100,3 +100,74 @@ function onSubmitEmailInSystem() } } +function checkPhoneAlreadyInSystem(phone, sessionCookie) +{ + if (phone == '') + { + return; + } + + var http = AJAX_getXMLHttpObject(); + + /* Build HTTP POST data. */ + var POSTData = '&phone=' + urlEncode(phone); + + /* Anonymous callback function triggered when HTTP response is received. */ + var callBack = function () + { + if (http.readyState != 4) + { + return; + } + + if (!http.responseXML) + { + var errorMessage = "An error occurred while receiving a response from the server.\n\n" + + http.responseText; + /* alert(errorMessage); */ + return; + } + + var idNode = http.responseXML.getElementsByTagName('id').item(0); + + if (idNode.firstChild.nodeValue != -1) + { + candidateIsAlreadyInSystem = true; + candidateIsAlreadyInSystemID = idNode.firstChild.nodeValue; + candidateIsAlreadyInSystemName = http.responseXML.getElementsByTagName('name').item(0).firstChild.nodeValue; + + document.getElementById('candidateAlreadyInSystemName').innerHTML = candidateIsAlreadyInSystemName; + document.getElementById('candidateAlreadyInSystemTable').style.display = ''; + } + else + { + candidateIsAlreadyInSystem = false; + document.getElementById('candidateAlreadyInSystemTable').style.display = 'none'; + } + } + + AJAX_callCATSFunction( + http, + 'getCandidateIdByPhone', + POSTData, + callBack, + 0, + sessionCookie, + false, + false + ); +} + +function onSubmitPhoneInSystem() +{ + if (candidateIsAlreadyInSystem) + { + var agree=confirm("Warning: The candidate may already be in the system.\n\nAre you sure you want to add the candidate?"); + if (agree) + return true ; + else + return false ; + } +} + + diff --git a/lib/CATSUtility.php b/lib/CATSUtility.php index ba6fae1f2..2feb4ffe5 100755 --- a/lib/CATSUtility.php +++ b/lib/CATSUtility.php @@ -316,7 +316,7 @@ public static function getIndexName() $indexParts = explode('.', $index); if ($indexParts[0] == 'ajax') { - return 'index' . $indexParts[1]; + return 'index.' . $indexParts[1]; } /* Older versions of apache sometimes don't concatinate script name by default. */ diff --git a/lib/Candidates.php b/lib/Candidates.php index 04eeae4f8..4c64226c8 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -679,6 +679,36 @@ public function getIDByEmail($email) return $rs['candidateID']; } + public function getIDByPhone($phone) + { + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID + FROM + candidate + WHERE + ( + candidate.phone_home = %s + OR candidate.phone_cell = %s + OR candidate.phone_work = %s + ) + AND + candidate.site_id = %s", + $this->_db->makeQueryString($phone), + $this->_db->makeQueryString($phone), + $this->_db->makeQueryString($phone), + $this->_siteID + ); + $rs = $this->_db->getAssoc($sql); + + if (empty($rs)) + { + return -1; + } + + return $rs['candidateID']; + } + /** * Returns the number of candidates in the system. Useful @@ -1156,7 +1186,8 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) 'Work Phone' => array('select' => 'candidate.phone_work AS phoneWork', 'sortableColumn' => 'phoneWork', - 'pagerWidth' => 80), + 'pagerWidth' => 80, + 'filter' => 'candidate.phone_work'), 'Address' => array('select' => 'candidate.address AS address', 'sortableColumn' => 'address', diff --git a/modules/candidates/Add.tpl b/modules/candidates/Add.tpl index 19aa65827..8cb678acf 100755 --- a/modules/candidates/Add.tpl +++ b/modules/candidates/Add.tpl @@ -43,7 +43,7 @@ -
+ isModal): ?> @@ -182,7 +182,7 @@ - + @@ -200,7 +200,7 @@ - + isParsingEnabled): ?> parsingStatus['parseLimit'] >= 0 && $this->parsingStatus['parseUsed'] >= $this->parsingStatus['parseLimit']): ?>   @@ -219,7 +219,7 @@ - + @@ -228,7 +228,7 @@ - + @@ -520,6 +520,18 @@ preassignedFields['email']) || isset($this->preassignedFields['email1'])): ?> checkEmailAlreadyInSystem(urlDecode("preassignedFields['email'])) echo(urlencode($this->preassignedFields['email'])); else if(isset($this->preassignedFields['email1'])) echo(urlencode($this->preassignedFields['email1'])); ?>")); + preassignedFields['email2']) || isset($this->preassignedFields['email2'])): ?> + checkEmailAlreadyInSystem(urlDecode("preassignedFields['email2'])) echo(urlencode($this->preassignedFields['email2'])); else if(isset($this->preassignedFields['email2'])) echo(urlencode($this->preassignedFields['email2'])); ?>")); + + preassignedFields['phoneCell']) || isset($this->preassignedFields['phoneCell'])): ?> + checkEmailAlreadyInSystem(urlDecode("preassignedFields['phoneCell'])) echo(urlencode($this->preassignedFields['phoneCell'])); else if(isset($this->preassignedFields['phoneCell'])) echo(urlencode($this->preassignedFields['phoneCell'])); ?>")); + + preassignedFields['phoneWork']) || isset($this->preassignedFields['phoneWork'])): ?> + checkEmailAlreadyInSystem(urlDecode("preassignedFields['phoneWork'])) echo(urlencode($this->preassignedFields['phoneWork'])); else if(isset($this->preassignedFields['phoneWork'])) echo(urlencode($this->preassignedFields['phoneWork'])); ?>")); + + preassignedFields['phoneHome']) || isset($this->preassignedFields['phoneHome'])): ?> + checkEmailAlreadyInSystem(urlDecode("preassignedFields['phoneHome'])) echo(urlencode($this->preassignedFields['phoneHome'])); else if(isset($this->preassignedFields['phoneHome'])) echo(urlencode($this->preassignedFields['phoneHome'])); ?>")); + isModal): ?> From 0616b6c278ba5e2a9910de7a088ee37783d5fbfb Mon Sep 17 00:00:00 2001 From: wjcheers Date: Thu, 24 Sep 2015 11:41:14 +0800 Subject: [PATCH 7/7] Make CSV exports in UTF-8 readable for Excel #41 Insert UTF-8 BOM before CSV files --- config.php | 8 ++++++++ lib/DataGrid.php | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/config.php b/config.php index 76bfc47e0..6be76ebf7 100755 --- a/config.php +++ b/config.php @@ -121,6 +121,14 @@ /* AJAX Encoding. */ define('AJAX_ENCODING', 'UTF-8'); +/* Insert BOM in the beginning of CSV file */ +/* This is UTF-8 BOM, EF BB BF for UTF-8 */ +define('INSERT_BOM_CSV_LENGTH', '3'); +define('INSERT_BOM_CSV_1', '239'); +define('INSERT_BOM_CSV_2', '187'); +define('INSERT_BOM_CSV_3', '191'); +define('INSERT_BOM_CSV_4', ''); + /* Path to modules. */ define('MODULES_PATH', './modules/'); diff --git a/lib/DataGrid.php b/lib/DataGrid.php index bde91655d..30ce14b49 100755 --- a/lib/DataGrid.php +++ b/lib/DataGrid.php @@ -1470,6 +1470,23 @@ public function drawCSV() header('Connection: close'); header('Content-Type: text/x-csv; name=export.csv'); + if (defined('INSERT_BOM_CSV_LENGTH') && (INSERT_BOM_CSV_LENGTH > 0)) + { + echo chr(INSERT_BOM_CSV_1); + if (INSERT_BOM_CSV_LENGTH > 1) + { + echo chr(INSERT_BOM_CSV_2); + } + if (INSERT_BOM_CSV_LENGTH > 2) + { + echo chr(INSERT_BOM_CSV_3); + } + if (INSERT_BOM_CSV_LENGTH > 3) + { + echo chr(INSERT_BOM_CSV_4); + } + } + echo $headerRow; foreach ($this->_rs as $rowIndex => $row)