From 34b20ca70d4866ad08bc3f613d96f272e3dc07e0 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Tue, 29 Nov 2016 19:05:37 +0100 Subject: [PATCH 01/92] toggle quickactionmenu visibility by clicking it --- js/lib.js | 11 +++++++++++ lib/TemplateUtility.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/js/lib.js b/js/lib.js index 5e44c7f99..a965f3448 100755 --- a/js/lib.js +++ b/js/lib.js @@ -49,6 +49,17 @@ var CATSIndexName; /* Default timeout for AJAX requests; 15 seconds. */ var AJAX_TIMEOUT = 15000; +function toggleVisibility() +{ + var singleQuickActionMenu = document.getElementById('singleQuickActionMenu'); + + if (singleQuickActionMenu.style.display == 'block') + { + singleQuickActionMenu.style.display = 'none'; + return; + } +} + /** * Returns true if the string is a valid positive integer. * diff --git a/lib/TemplateUtility.php b/lib/TemplateUtility.php index a37dd1f1f..667437459 100755 --- a/lib/TemplateUtility.php +++ b/lib/TemplateUtility.php @@ -1130,7 +1130,7 @@ public static function printSingleQuickActionMenu($dataItemType, $dataItemID) public static function _printQuickActionMenuHolder() { - echo '
'; + echo '
'; echo '
'; } From c7ae52c9c3ff4bc7fc5dde71ff91644ca916fbb9 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Tue, 29 Nov 2016 19:19:47 +0100 Subject: [PATCH 02/92] Candidate duplicates module added and integrated with other modules --- constants.php | 1 + db/cats_schema.sql | 11 + images/actions/duplicates.png | Bin 0 -> 157485 bytes js/lib.js | 1 + js/quickAction.js | 62 +- lib/Candidates.php | 211 +++++ lib/Duplicates.php | 1273 ++++++++++++++++++++++++++ lib/Search.php | 45 +- lib/TemplateUtility.php | 13 +- modules/candidates/CandidatesUI.php | 12 + modules/candidates/Show.tpl | 6 + modules/duplicates/Duplicates.tpl | 158 ++++ modules/duplicates/DuplicatesUI.php | 694 ++++++++++++++ modules/duplicates/Error.tpl | 37 + modules/duplicates/LinkDuplicity.tpl | 161 ++++ modules/duplicates/Merge.tpl | 175 ++++ modules/duplicates/Show.tpl | 547 +++++++++++ modules/duplicates/dataGrids.php | 141 +++ 18 files changed, 3544 insertions(+), 4 deletions(-) create mode 100644 images/actions/duplicates.png create mode 100644 lib/Duplicates.php create mode 100644 modules/duplicates/Duplicates.tpl create mode 100644 modules/duplicates/DuplicatesUI.php create mode 100644 modules/duplicates/Error.tpl create mode 100644 modules/duplicates/LinkDuplicity.tpl create mode 100644 modules/duplicates/Merge.tpl create mode 100644 modules/duplicates/Show.tpl create mode 100644 modules/duplicates/dataGrids.php diff --git a/constants.php b/constants.php index ceb743ee9..44fe92248 100644 --- a/constants.php +++ b/constants.php @@ -60,6 +60,7 @@ define('DATA_ITEM_USER', 600); define('DATA_ITEM_LIST', 700); define('DATA_ITEM_PIPELINE', 800); +define('DATA_ITEM_DUPLICATE', 900); /* Settings types. */ define('SETTINGS_MAILER', 1); diff --git a/db/cats_schema.sql b/db/cats_schema.sql index 55b047c26..ce19fee4b 100755 --- a/db/cats_schema.sql +++ b/db/cats_schema.sql @@ -211,6 +211,17 @@ CREATE TABLE `candidate` ( /*Data for the table `candidate` */ +/*Table structure for table `candidate_duplicates` */ + +CREATE TABLE `candidate_duplicates` ( + `old_candidate_id` int(11) NOT NULL, + `new_candidate_id` int(11) NOT NULL, + `site_id` int(11) NOT NULL, + PRIMARY KEY (`old_candidate_id`, `new_candidate_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +/*Data for the table `candidate_duplicates` */ + /*Table structure for table `candidate_joborder` */ CREATE TABLE `candidate_joborder` ( diff --git a/images/actions/duplicates.png b/images/actions/duplicates.png new file mode 100644 index 0000000000000000000000000000000000000000..ad0c97279a0ccd2ec24acfa5509e559e3cd14f01 GIT binary patch literal 157485 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 literal 0 HcmV?d00001 diff --git a/js/lib.js b/js/lib.js index a965f3448..4010469e7 100755 --- a/js/lib.js +++ b/js/lib.js @@ -42,6 +42,7 @@ var DATA_ITEM_CANDIDATE = 100; var DATA_ITEM_COMPANY = 200; var DATA_ITEM_CONTACT = 300; var DATA_ITEM_JOBORDER = 400; +var DATA_ITEM_DUPLICATE = 900; /* Set by TemplateUtility drawing headers. */ var CATSIndexName; diff --git a/js/quickAction.js b/js/quickAction.js index 5bff2aec9..b03d6ea7f 100755 --- a/js/quickAction.js +++ b/js/quickAction.js @@ -45,16 +45,53 @@ function showHideSingleQuickActionMenu(dataItemType, dataItemID, menuX, menuY) _singleQuickActionMenuDataItemType = dataItemType; _singleQuickActionMenuDataItemID = dataItemID; - addItemToPopupMenu('Add To List', 'showQuickActionAddToList();'); + switch (dataItemType) { case DATA_ITEM_CANDIDATE: + addItemToPopupMenu('Add To List', 'showQuickActionAddToList();'); addItemToPopupMenu('Add To Pipeline', 'showQuickActionAddToPipeline();'); break; + default: + addItemToPopupMenu('Add To List', 'showQuickActionAddToList();'); } } +function showHideSingleQuickActionMenuExtended(dataItemType, dataItemID, menuX, menuY, url1, url2) +{ + var singleQuickActionMenu = document.getElementById('singleQuickActionMenu'); + + if (singleQuickActionMenu.style.display == 'block') + { + closeQuickActionMenu(); + return; + } + + singleQuickActionMenu.style.display = 'block'; + singleQuickActionMenu.style.left = menuX + 'px'; + singleQuickActionMenu.style.top = menuY + 'px'; + singleQuickActionMenu.innerHTML = ''; + _singleQuickActionMenuDataItemType = dataItemType; + _singleQuickActionMenuDataItemID = dataItemID; + + + + switch (dataItemType) + { + case DATA_ITEM_DUPLICATE: + addLinkToPopupMenu('Merge', urldecode(url1), 0); + addLinkToPopupMenu('Remove duplicity warning', urldecode(url2), 1); + break; + default: + addItemToPopupMenu('Add To List', 'showQuickActionAddToList();'); + } +} + +function urldecode(url) { + return decodeURIComponent(url.replace(/\+/g, ' ')); +} + /* Shows a popup for adding a item to a list. */ function showQuickActionAddToList() { @@ -76,9 +113,32 @@ function addItemToPopupMenu(itemTitle, itemAction) singleQuickActionMenu.innerHTML += '' + itemTitle + '
'; } +function addLinkToPopupMenu(itemTitle, itemAction, option) +{ + var singleQuickActionMenu = document.getElementById('singleQuickActionMenu'); + var message = "'Are you sure?'"; + switch(option) + { + case 0: + itemAction = "'" + itemAction + "'"; + singleQuickActionMenu.innerHTML += '' + itemTitle + '
'; + break; + case 1: + default: + singleQuickActionMenu.innerHTML += '' + itemTitle + '
'; + break; + } + +} + function closeQuickActionMenu() { var singleQuickActionMenu = document.getElementById('singleQuickActionMenu'); singleQuickActionMenu.style.display = 'none'; } +function mergeCandidates() +{ + +} + diff --git a/lib/Candidates.php b/lib/Candidates.php index 4c64226c8..a3759de29 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -419,6 +419,19 @@ public function delete($candidateID) DATA_ITEM_CANDIDATE ); $this->_db->query($sql); + + /* Delete from candidate_duplicates. */ + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + old_candidate_id = %s + OR + new_candidate_id = %s", + $this->_db->makeQueryInteger($candidateID), + $this->_db->makeQueryInteger($candidateID) + ); + $this->_db->query($sql); /* Delete attachments. */ $attachments = new Attachments($this->_siteID); @@ -543,6 +556,128 @@ public function get($candidateID) return $this->_db->getAssoc($sql); } + + public function getWithDuplicity($candidateID) + { + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.is_active AS isActive, + candidate.first_name AS firstName, + candidate.middle_name AS middleName, + candidate.last_name AS lastName, + candidate.email1 AS email1, + candidate.email2 AS email2, + candidate.phone_home AS phoneHome, + candidate.phone_work AS phoneWork, + candidate.phone_cell AS phoneCell, + candidate.address AS address, + candidate.city AS city, + candidate.state AS state, + candidate.zip AS zip, + candidate.source AS source, + candidate.key_skills AS keySkills, + candidate.current_employer AS currentEmployer, + candidate.current_pay AS currentPay, + candidate.desired_pay AS desiredPay, + candidate.notes AS notes, + candidate.owner AS owner, + candidate.can_relocate AS canRelocate, + candidate.web_site AS webSite, + candidate.best_time_to_call AS bestTimeToCall, + candidate.is_hot AS isHot, + candidate.is_admin_hidden AS isAdminHidden, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y (%%h:%%i %%p)' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y (%%h:%%i %%p)' + ) AS dateModified, + COUNT( + candidate_joborder.joborder_id + ) AS pipeline, + ( + SELECT + COUNT(*) + FROM + candidate_joborder_status_history + WHERE + candidate_id = %s + AND + status_to = %s + AND + site_id = %s + ) AS submitted, + CONCAT( + candidate.first_name, ' ', candidate.last_name + ) AS candidateFullName, + CONCAT( + entered_by_user.first_name, ' ', entered_by_user.last_name + ) AS enteredByFullName, + CONCAT( + owner_user.first_name, ' ', owner_user.last_name + ) AS ownerFullName, + owner_user.email AS owner_email, + DATE_FORMAT( + candidate.date_available, '%%m-%%d-%%y' + ) AS dateAvailable, + eeo_ethnic_type.type AS eeoEthnicType, + eeo_veteran_type.type AS eeoVeteranType, + candidate.eeo_disability_status AS eeoDisabilityStatus, + candidate.eeo_gender AS eeoGender, + IF (candidate.eeo_gender = 'm', + 'Male', + IF (candidate.eeo_gender = 'f', + 'Female', + '')) + AS eeoGenderText + FROM + candidate + LEFT JOIN user AS entered_by_user + ON candidate.entered_by = entered_by_user.user_id + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + LEFT JOIN candidate_joborder + ON candidate.candidate_id = candidate_joborder.candidate_id + LEFT JOIN eeo_ethnic_type + ON eeo_ethnic_type.eeo_ethnic_type_id = candidate.eeo_ethnic_type_id + LEFT JOIN eeo_veteran_type + ON eeo_veteran_type.eeo_veteran_type_id = candidate.eeo_veteran_type_id + WHERE + candidate.candidate_id = %s + AND + candidate.site_id = %s + GROUP BY + candidate.candidate_id", + $this->_db->makeQueryInteger($candidateID), + PIPELINE_STATUS_SUBMITTED, + $this->_siteID, + $this->_db->makeQueryInteger($candidateID), + $this->_siteID + ); + $data = $this->_db->getAssoc($sql); + + $sql = sprintf( + "SELECT + candidate_duplicates.old_candidate_id AS duplicateTo + FROM + candidate_duplicates + WHERE + candidate_duplicates.new_candidate_id = %s", + $this->_db->makeQueryInteger($candidateID) + ); + $rs = $this->_db->getAllAssoc($sql); + $temp = array(); + if($rs && !$this->_db->isEOF()) + { + foreach($rs as $row) + { + array_push($temp, $row); + } + } + $data['isDuplicate'] = $temp; + return $data; + } /** * Returns all candidate information relevent for the Edit Candidate page @@ -1093,6 +1228,82 @@ public function administrativeHideShow($candidateID, $state) return (boolean) $this->_db->query($sql); } + + public function checkDuplicity($firstName, $middleName, $lastName, $email1, $email2, $phoneHome, $phoneCell, $phoneWork, $address, $city) + { + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.middle_name AS middleName, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.phone_work AS phoneWork, + candidate.email1 AS email1, + candidate.email2 AS email2, + candidate.address AS address, + candidate.city AS city + FROM + candidate + WHERE + candidate.first_name = %s AND + candidate.last_name = %s", + $this->_db->makeQueryStringOrNULL($firstName), + $this->_db->makeQueryStringOrNULL($lastName) + ); + + $rs = $this->_db->getAllAssoc($sql); + + $duplicatesID = array(); + + if($rs && !$this->_db->isEOF()) + { + $phoneNumbers = array(); + + if($phoneHome != ""){array_push($phoneNumbers, $phoneHome);} + if($phoneCell != ""){array_push($phoneNumbers, $phoneCell);} + if($phoneWork != ""){array_push($phoneNumbers, $phoneWork);} + + $phoneNumbers = array_map('strtolower', $phoneNumbers); + + + foreach($rs as $row) + { + $phoneNumbersDB = array(); + if($row['phoneHome'] != ""){array_push($phoneNumbersDB, $row['phoneHome']);} + if($row['phoneCell'] != ""){array_push($phoneNumbersDB, $row['phoneCell']);} + if($row['phoneWork'] != ""){array_push($phoneNumbersDB, $row['phoneWork']);} + $phoneNumbersDB = array_map('strtolower', $phoneNumbersDB); + + if (strtolower($row['middleName']) == strtolower($middleName) && $middleName != "") + { + array_push($duplicatesID, $row['candidateID']); + } + else if(sizeof(array_diff($phoneNumbers, $phoneNumbersDB)) != sizeof($phoneNumbers) || sizeof(array_diff($phoneNumbersDB, $phoneNumbers)) != sizeof($phoneNumbersDB)) + { + array_push($duplicatesID, $row['candidateID']); + } + else if((strtolower($email1) == strtolower($row['email1']) && $email1!= "" ) || (strtolower($email1) == strtolower($row['email2']) && $email1!= "") || + (strtolower($email2) == strtolower($row['email1']) && $email2!= "" ) || (strtolower($email2) == strtolower($row['email2'])) && $email2!= "") + { + array_push($duplicatesID, $row['candidateID']); + } + else if(strtolower($city) == strtolower($row['city']) && $city != "") + { + if(strtolower($address) == strtolower[$row['address']] && $address != "") + { + array_push($duplicatesID, $row['candidateID']); + } + } + } + + return $duplicatesID; + + } + else + { + return $duplicatesID; + } + } } diff --git a/lib/Duplicates.php b/lib/Duplicates.php new file mode 100644 index 000000000..4ca333dc3 --- /dev/null +++ b/lib/Duplicates.php @@ -0,0 +1,1273 @@ +_siteID = $siteID; + $this->_db = DatabaseConnection::getInstance(); + $this->extraFields = new ExtraFields($siteID, DATA_ITEM_CANDIDATE); + } + + + + + /** + * Removes a candidate and all associated records from the system. + * + * @param integer Candidate ID to delete. + * @return void + */ + public function delete($firstCandidateID, $secondCandidateID) + { + /* Delete the duplicate from candidate_duplicates. */ + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + old_candidate_id = %s + AND + new_candidate_id = %s + OR + new_candidate_id = %s + AND + old_candidate_id = %s" + , + $this->_db->makeQueryInteger($firstCandidateID), + $this->_db->makeQueryInteger($secondCandidateID), + $this->_db->makeQueryInteger($firstCandidateID), + $this->_db->makeQueryInteger($secondCandidateID) + ); + $this->_db->query($sql); + } + + /** + * Returns all relevent candidate information for a given candidate ID. + * + * @param integer Candidate ID. + * @return array Associative result set array of candidate data, or array() + * if no records were returned. + */ + public function get($candidateID) + { + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.is_active AS isActive, + candidate.first_name AS firstName, + candidate.middle_name AS middleName, + candidate.last_name AS lastName, + candidate.email1 AS email1, + candidate.email2 AS email2, + candidate.phone_home AS phoneHome, + candidate.phone_work AS phoneWork, + candidate.phone_cell AS phoneCell, + candidate.address AS address, + candidate.city AS city, + candidate.state AS state, + candidate.zip AS zip, + candidate.source AS source, + candidate.key_skills AS keySkills, + candidate.current_employer AS currentEmployer, + candidate.current_pay AS currentPay, + candidate.desired_pay AS desiredPay, + candidate.notes AS notes, + candidate.owner AS owner, + candidate.can_relocate AS canRelocate, + candidate.web_site AS webSite, + candidate.best_time_to_call AS bestTimeToCall, + candidate.is_hot AS isHot, + candidate.is_admin_hidden AS isAdminHidden, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y (%%h:%%i %%p)' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y (%%h:%%i %%p)' + ) AS dateModified, + COUNT( + candidate_joborder.joborder_id + ) AS pipeline, + ( + SELECT + COUNT(*) + FROM + candidate_joborder_status_history + WHERE + candidate_id = %s + AND + status_to = %s + AND + site_id = %s + ) AS submitted, + CONCAT( + candidate.first_name, ' ', candidate.last_name + ) AS candidateFullName, + CONCAT( + entered_by_user.first_name, ' ', entered_by_user.last_name + ) AS enteredByFullName, + CONCAT( + owner_user.first_name, ' ', owner_user.last_name + ) AS ownerFullName, + owner_user.email AS owner_email, + DATE_FORMAT( + candidate.date_available, '%%m-%%d-%%y' + ) AS dateAvailable, + eeo_ethnic_type.type AS eeoEthnicType, + eeo_veteran_type.type AS eeoVeteranType, + candidate.eeo_disability_status AS eeoDisabilityStatus, + candidate.eeo_gender AS eeoGender, + IF (candidate.eeo_gender = 'm', + 'Male', + IF (candidate.eeo_gender = 'f', + 'Female', + '')) + AS eeoGenderText + FROM + candidate + LEFT JOIN user AS entered_by_user + ON candidate.entered_by = entered_by_user.user_id + LEFT JOIN user AS owner_user + ON candidate.owner = owner_user.user_id + LEFT JOIN candidate_joborder + ON candidate.candidate_id = candidate_joborder.candidate_id + LEFT JOIN eeo_ethnic_type + ON eeo_ethnic_type.eeo_ethnic_type_id = candidate.eeo_ethnic_type_id + LEFT JOIN eeo_veteran_type + ON eeo_veteran_type.eeo_veteran_type_id = candidate.eeo_veteran_type_id + WHERE + candidate.candidate_id = %s + AND + candidate.site_id = %s + GROUP BY + candidate.candidate_id", + $this->_db->makeQueryInteger($candidateID), + PIPELINE_STATUS_SUBMITTED, + $this->_siteID, + $this->_db->makeQueryInteger($candidateID), + $this->_siteID + ); + $data = $this->_db->getAssoc($sql); + + $sql = sprintf( + "SELECT + candidate_duplicates.old_candidate_id AS duplicateTo + FROM + candidate_duplicates + WHERE + candidate_duplicates.new_candidate_id = %s", + $this->_db->makeQueryInteger($candidateID) + ); + $rs = $this->_db->getAllAssoc($sql); + $temp = array(); + if($rs && !$this->_db->isEOF()) + { + foreach($rs as $row) + { + array_push($temp, $row); + } + } + $data['isDuplicate'] = $temp; + return $data; + } + + + // FIXME: Document me. + public function getExport($IDs) + { + if (count($IDs) != 0) + { + $IDsValidated = array(); + + foreach ($IDs as $id) + { + $IDsValidated[] = $this->_db->makeQueryInteger($id); + } + + $criterion = 'AND candidate.candidate_id IN ('.implode(',', $IDsValidated).')'; + } + else + { + $criterion = ''; + } + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.last_name AS lastName, + candidate.first_name AS firstName, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.email1 AS email1, + candidate.key_skills as keySkills + FROM + candidate + WHERE + candidate.site_id = %s + %s + ORDER BY + candidate.last_name ASC, + candidate.first_name ASC", + $this->_siteID, + $criterion + ); + + return $this->_db->getAllAssoc($sql); + } + + /** + * Returns a candidate ID that matches the specified e-mail address. + * + * @param string Candidate e-mail address, + * @return integer Candidate ID, or -1 if no matching candidates were + * found. + */ + public function getIDByEmail($email) + { + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID + FROM + candidate + WHERE + ( + candidate.email1 = %s + OR candidate.email2 = %s + ) + AND + candidate.site_id = %s", + $this->_db->makeQueryString($email), + $this->_db->makeQueryString($email), + $this->_siteID + ); + $rs = $this->_db->getAssoc($sql); + + if (empty($rs)) + { + return -1; + } + + 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 duplicates in the system. Useful + * for determining if the friendly "no candidates in system" + * should be displayed rather than the datagrid. + * + * @return integer Number of Duplicates in site. + */ + public function getCount() + { + $sql = sprintf( + "SELECT + COUNT(*) AS totalDuplicates + FROM + (SELECT * + FROM candidate_duplicates + WHERE + candidate_duplicates.site_id = %s + GROUP BY + candidate_duplicates.new_candidate_id) as innerQuery", + $this->_siteID + ); + return $this->_db->getColumn($sql, 0, 0); + } + + /** + * Returns the entire candidates list. + * + * @param boolean Include administratively hidden candidates? + * @return array Multi-dimensional associative result set array of + * candidates data, or array() if no records were returned. + */ + public function getAll($allowAdministrativeHidden = false) + { + if (!$allowAdministrativeHidden) + { + $adminHiddenCriterion = 'AND candidate.is_admin_hidden = 0'; + } + else + { + $adminHiddenCriterion = ''; + } + + $sql = sprintf( + "SELECT + candidate.candidate_id AS candidateID, + candidate.last_name AS lastName, + candidate.first_name AS firstName, + candidate.phone_home AS phoneHome, + candidate.phone_cell AS phoneCell, + candidate.email1 AS email1, + candidate.key_skills AS keySkills, + candidate.is_hot AS isHot, + DATE_FORMAT( + candidate.date_created, '%%m-%%d-%%y' + ) AS dateCreated, + DATE_FORMAT( + candidate.date_modified, '%%m-%%d-%%y' + ) AS dateModified, + candidate.date_created AS dateCreatedSort, + owner_user.first_name AS ownerFirstName, + owner_user.last_name AS ownerLastName + FROM + candidate + LEFT JOIN user AS owner_user + ON candidate.entered_by = user.user_id + WHERE + candidate.site_id = %s + %s + ORDER BY + candidate.last_name ASC, + candidate.first_name ASC", + $this->_siteID, + $adminHiddenCriterion + ); + + return $this->_db->getAllAssoc($sql); + } + + + + /** + * Updates a candidate's modified timestamp. + * + * @param integer Candidate ID. + * @return boolean Boolean was the query executed successfully? + */ + public function updateModified($candidateID) + { + $sql = sprintf( + "UPDATE + candidate + SET + date_modified = NOW() + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($candidateID), + $this->_siteID + ); + + return (boolean) $this->_db->query($sql); + } + + public function removeDuplicity($oldCandidateID, $newCandidateID) + { + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + candidate_duplicates.old_candidate_id = %s + AND + candidate_duplicates.new_candidate_id = %s", + $this->_db->makeQueryStringOrNULL($oldCandidateID), + $this->_db->makeQueryStringOrNULL($newCandidateID) + ); + $this->_db->query($sql); + } + + /** + * Adds a duplicate to the database. + * + * @param string first candidate ID. + * @param string second candidate ID. + * @return integer 1 on success, or -1 on failure. + */ + + public function addDuplicates($candidateID, $duplicates) + { + if(is_array($duplicates)) + { + foreach($duplicates as $duplicateID) + { + $sql = sprintf( + "INSERT INTO candidate_duplicates ( + old_candidate_id, + new_candidate_id, + site_id + ) + VALUES ( + %s, + %s, + %s + )", + $this->_db->makeQueryString($duplicateID), + $this->_db->makeQueryString($candidateID), + $this->_siteID + ); + $this->_db->query($sql); + } + } + else if($duplicates != "") + { + $sql = sprintf( + "INSERT INTO candidate_duplicates ( + old_candidate_id, + new_candidate_id, + site_id + ) + VALUES ( + %s, + %s, + %s + )", + $this->_db->makeQueryString($duplicates), + $this->_db->makeQueryString($candidateID), + $this->_siteID + ); + $this->_db->query($sql); + } + } + + + + public function mergeDuplicates($params, $rs) + { + $oldCandidateID = $params['oldCandidateID']; + $newCandidateID = $params['newCandidateID']; + $sql = sprintf( + "UPDATE + activity + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + attachment + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + calendar_event + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + new_candidate_id = %s", + $this->_db->makeQueryInteger($newCandidateID) + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_duplicates + SET + old_candidate_id = %s + WHERE + old_candidate_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID) + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_joborder + SET + candidate_id = %s + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_joborder_status_history + SET + candidate_id = %s + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_tag + SET + candidate_id = %s + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + saved_list_entry + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + echo $this->_db->query($sql); + + + $update = " "; + $comma = false; + + if($params['firstName'] == "1") + { + $update .= "first_name = '" . $rs['firstName']."'"; + $comma = true; + } + if($params['middleName'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "middle_name = '" . $rs['middleName']."'"; + $comma = true; + } + if($params['lastName'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "last_name = '" . $rs['lastName']."'"; + $comma = true; + } + if($params['phoneCell'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "phone_cell = '" . $rs['phoneCell']."'"; + $comma = true; + } + if($params['phoneWork'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "phone_work = '" . $rs['phoneWork']."'"; + $comma = true; + } + if($params['phoneHome'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "phone_home = '" . $rs['phoneHome']."'"; + $comma = true; + } + if($params['address'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "address = '" . $rs['address'] . "', city = '" . $rs['city'] . "', zip = '" . $rs['zip'] . "', state = '" . $rs['state'] . "'"; + $comma = true; + } + if($params['website'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "web_site = '" . $rs['webSite'] . "'"; + $comma = true; + } + if(sizeof($params['emails']) == 1) + { + if($comma) + { + $update .= ", "; + } + $update .= "email1 = '" . $params['emails'][0]."'"; + $comma = true; + }else if(sizeof($params['emails']) == 2) + { + if($comma) + { + $update .= ", "; + $comma = false; + } + if($params['emails'][0] != "") + { + $update .= "email1 = '" . $params['emails'][0] . "'"; + $comma = true; + } + if(comma) + { + $update .= ", "; + } + if($params['emails'][1] != "") + { + $update .= "email2 = '" . $params['emails'][1] . "'"; + } + $comma = true; + } + if(comma){ + $update .= ", "; + } + $dateAvailable = $rs['dateAvailable']; + $dateParts = explode("-", $dateAvailable); + $dateAvailable = "20" . $dateParts[2] . "-" . $dateParts[0] . "-" . $dateParts[1] . " 00:00:00"; + $update .= "is_active = " . $rs['isActive'] . ", " . + "current_employer = '" . $rs['currentEmployer'] . "', " . + "current_pay = '" . $rs['currentPay'] . "', " . + "desired_pay = '" . $rs['desiredPay'] . "', " . + "can_relocate = " . $rs['canRelocate'] . ", " . + "best_time_to_call = '" . $rs['bestTimeToCall'] . "', " . + "is_hot = " . $rs['isHot'] . ", " . + "date_modified = NOW()"; + $comma = true; + if($rs['source'] != "" && $rs['source'] != "(none)") + { + if($comma){$update .= ", ";} + $update.= "source = IFNULL(CONCAT(source, ', ".$rs['source'] . "'), '" . $rs['source'] . "')"; + $comma = true; + } + if($rs['keySkills'] != "") + { + if($comma){$update .= ", ";} + $update .= "key_skills = IFNULL(CONCAT(key_skills, ', ".$rs['keySkills']."'), '" . $rs['keySkills'] . "')"; + $comma = true; + } + if($rs['notes'] != "") + { + if($comma){$update .= ", ";} + $update .= "notes = IFNULL(CONCAT(notes, ', ".$rs['notes']."'), '" . $rs['notes'] . "')"; + $comma = true; + } + if($rs['date_available'] != "") + { + if($comma){$update .= ", ";} + $update .= "date_available = '".$dateAvailable."' "; + } + + $sql = sprintf( + "UPDATE + candidate + SET + %s + WHERE + candidate_id = %s + AND + site_id = %s", + $update, + $this->_db->makeQueryInteger($oldCandidateID), + $this->_siteID + ); + + if($this->_db->query($sql)) + { + $sql = sprintf( + "DELETE FROM + candidate + WHERE + candidate_id = %s", + $this->_db->makeQueryInteger($newCandidateID) + ); + echo $this->_db->query($sql); + } + + //TO-DO: delete new candidate + } + + public function checkIfLinked($oldCandidateID, $newCandidateID) + { + if($oldCandidateID == $newCandidateID) + { + return true; + } + + $sql = sprintf( + "SELECT + candidate_duplicates.old_candidate_id as oldCandidateID, + candidate_duplicates.new_candidate_id as newCandidateID + FROM + candidate_duplicates + WHERE + candidate_duplicates.old_candidate_id = %s + AND + candidate_duplicates.new_candidate_id = %s + OR + candidate_duplicates.old_candidate_id = %s + AND + candidate_duplicates.new_candidate_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_db->makeQueryInteger($oldCandidateID) + ); + $rs = $this->_db->getAllAssoc($sql); + + if($rs && !$this->_db->isEOF()) + { + return true; + } + else + { + return false; + } + } +} + + +class DuplicatesDataGrid extends DataGrid +{ + protected $_siteID; + + // FIXME: Fix ugly indenting - ~400 character lines = bad. + public function __construct($instanceName, $siteID, $parameters, $misc = 0) + { + $this->_db = DatabaseConnection::getInstance(); + $this->_siteID = $siteID; + $this->_assignedCriterion = ""; + $this->_dataItemIDColumn = 'candidate.candidate_id'; + + $this->_classColumns = array( + 'Attachments' => array('select' => 'IF(candidate_joborder_submitted.candidate_joborder_id, 1, 0) AS submitted, + IF(attachment_id, 1, 0) AS attachmentPresent', + + 'pagerRender' => 'if ($rsData[\'submitted\'] == 1) + { + $return = \'\'; + } + else + { + $return = \'\'; + } + + if ($rsData[\'attachmentPresent\'] == 1) + { + $return .= \'\'; + } + else + { + $return .= \'\'; + } + + return $return; + ', + + 'join' => 'LEFT JOIN attachment + ON candidate.candidate_id = attachment.data_item_id + AND attachment.data_item_type = '.DATA_ITEM_CANDIDATE.' + LEFT JOIN candidate_joborder AS candidate_joborder_submitted + ON candidate_joborder_submitted.candidate_id = candidate.candidate_id + AND candidate_joborder_submitted.status >= '.PIPELINE_STATUS_SUBMITTED.' + AND candidate_joborder_submitted.site_id = '.$this->_siteID.' + AND candidate_joborder_submitted.status != '.PIPELINE_STATUS_NOTINCONSIDERATION.' + INNER JOIN candidate_duplicates + ON candidate.candidate_id = candidate_duplicates.new_candidate_id + ', + 'pagerWidth' => 70, + 'pagerOptional' => true, + 'pagerNoTitle' => true, + 'sizable' => false, + 'exportable' => false, + 'filterable' => false), + + 'First Name' => array('select' => 'candidate.first_name AS firstName', + 'pagerRender' => 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'firstName\']).\'\';', + 'sortableColumn' => 'firstName', + 'pagerWidth' => 75, + 'pagerOptional' => false, + 'alphaNavigation'=> true, + 'filter' => 'candidate.first_name'), + + 'Last Name' => array('select' => 'candidate.last_name AS lastName', + 'sortableColumn' => 'lastName', + 'pagerRender' => 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'lastName\']).\'\';', + 'pagerWidth' => 85, + 'pagerOptional' => false, + 'alphaNavigation' => true, + 'filter' => 'candidate.last_name'), + + 'E-Mail' => array('select' => 'candidate.email1 AS email1', + 'sortableColumn' => 'email1', + 'pagerWidth' => 80, + 'filter' => 'candidate.email1'), + + '2nd E-Mail' => array('select' => 'candidate.email2 AS email2', + 'sortableColumn' => 'email2', + 'pagerWidth' => 80, + 'filter' => 'candidate.email2'), + + 'Home Phone' => array('select' => 'candidate.phone_home AS phoneHome', + 'sortableColumn' => 'phoneHome', + 'pagerWidth' => 80, + 'filter' => 'candidate.phone_home'), + + 'Cell Phone' => array('select' => 'candidate.phone_cell AS phoneCell', + 'sortableColumn' => 'phoneCell', + 'pagerWidth' => 80, + 'filter' => 'candidate.phone_cell'), + + 'Work Phone' => array('select' => 'candidate.phone_work AS phoneWork', + 'sortableColumn' => 'phoneWork', + 'pagerWidth' => 80, + 'filter' => 'candidate.phone_work'), + + 'Address' => array('select' => 'candidate.address AS address', + 'sortableColumn' => 'address', + 'pagerWidth' => 250, + 'alphaNavigation' => true, + 'filter' => 'candidate.address'), + + 'City' => array('select' => 'candidate.city AS city', + 'sortableColumn' => 'city', + 'pagerWidth' => 80, + 'alphaNavigation' => true, + 'filter' => 'candidate.city'), + + + 'State' => array('select' => 'candidate.state AS state', + 'sortableColumn' => 'state', + 'filterType' => 'dropDown', + 'pagerWidth' => 50, + 'alphaNavigation' => true, + 'filter' => 'candidate.state'), + + 'Zip' => array('select' => 'candidate.zip AS zip', + 'sortableColumn' => 'zip', + 'pagerWidth' => 50, + 'filter' => 'candidate.zip'), + + 'Misc Notes' => array('select' => 'candidate.notes AS notes', + 'sortableColumn' => 'notes', + 'pagerWidth' => 300, + 'filter' => 'candidate.notes'), + + 'Web Site' => array('select' => 'candidate.web_site AS webSite', + 'pagerRender' => 'return \'\'.htmlspecialchars($rsData[\'webSite\']).\'\';', + 'sortableColumn' => 'webSite', + 'pagerWidth' => 80, + 'filter' => 'candidate.web_site'), + + 'Key Skills' => array('select' => 'candidate.key_skills AS keySkills', + 'pagerRender' => 'return substr(trim($rsData[\'keySkills\']), 0, 30) . (strlen(trim($rsData[\'keySkills\'])) > 30 ? \'...\' : \'\');', + 'sortableColumn' => 'keySkills', + 'pagerWidth' => 210, + 'filter' => 'candidate.key_skills'), + + 'Recent Status' => array('select' => '( + SELECT + CONCAT( + \'\', + candidate_joborder_status.short_description, + \'\' + ) + FROM + candidate_joborder + LEFT JOIN candidate_joborder_status + ON candidate_joborder_status.candidate_joborder_status_id = candidate_joborder.status + LEFT JOIN joborder + ON joborder.joborder_id = candidate_joborder.joborder_id + LEFT JOIN company + ON joborder.company_id = company.company_id + WHERE + candidate_joborder.candidate_id = candidate.candidate_id + ORDER BY + candidate_joborder.date_modified DESC + LIMIT 1 + ) AS lastStatus + ', + 'sort' => 'lastStatus', + 'pagerRender' => 'return $rsData[\'lastStatus\'];', + 'exportRender' => 'return $rsData[\'lastStatus\'];', + 'pagerWidth' => 140, + 'exportable' => false, + 'filterHaving' => 'lastStatus', + 'filterTypes' => '=~'), + + 'Recent Status (Extended)' => array('select' => '( + SELECT + CONCAT( + candidate_joborder_status.short_description, + \'
\', + \'\', + company.name, + \' - \', + \'\', + joborder.title, + \'\' + ) + FROM + candidate_joborder + LEFT JOIN candidate_joborder_status + ON candidate_joborder_status.candidate_joborder_status_id = candidate_joborder.status + LEFT JOIN joborder + ON joborder.joborder_id = candidate_joborder.joborder_id + LEFT JOIN company + ON joborder.company_id = company.company_id + WHERE + candidate_joborder.candidate_id = candidate.candidate_id + ORDER BY + candidate_joborder.date_modified DESC + LIMIT 1 + ) AS lastStatusLong + ', + 'sortableColumn' => 'lastStatusLong', + 'pagerRender' => 'return $rsData[\'lastStatusLong\'];', + 'pagerWidth' => 310, + 'exportable' => false, + 'filterable' => false), + + 'Source' => array('select' => 'candidate.source AS source', + 'sortableColumn' => 'source', + 'pagerWidth' => 140, + 'alphaNavigation' => true, + 'filter' => 'candidate.source'), + + 'Available' => array('select' => 'DATE_FORMAT(candidate.date_available, \'%m-%d-%y\') AS dateAvailable', + 'sortableColumn' => 'dateAvailable', + 'pagerWidth' => 60), + + 'Current Employer' => array('select' => 'candidate.current_employer AS currentEmployer', + 'sortableColumn' => 'currentEmployer', + 'pagerWidth' => 125, + 'alphaNavigation' => true, + 'filter' => 'candidate.current_employer'), + + 'Current Pay' => array('select' => 'candidate.current_pay AS currentPay', + 'sortableColumn' => 'currentPay', + 'pagerWidth' => 125, + 'filter' => 'candidate.current_pay', + 'filterTypes' => '===>=<'), + + 'Desired Pay' => array('select' => 'candidate.desired_pay AS desiredPay', + 'sortableColumn' => 'desiredPay', + 'pagerWidth' => 125, + 'filter' => 'candidate.desired_pay', + 'filterTypes' => '===>=<'), + + 'Can Relocate' => array('select' => 'candidate.can_relocate AS canRelocate', + 'pagerRender' => 'return ($rsData[\'canRelocate\'] == 0 ? \'No\' : \'Yes\');', + 'exportRender' => 'return ($rsData[\'canRelocate\'] == 0 ? \'No\' : \'Yes\');', + 'sortableColumn' => 'canRelocate', + 'pagerWidth' => 80, + 'filter' => 'candidate.can_relocate'), + + 'Owner' => array('select' => 'owner_user.first_name AS ownerFirstName,' . + 'owner_user.last_name AS ownerLastName,' . + 'CONCAT(owner_user.last_name, owner_user.first_name) AS ownerSort', + 'join' => 'LEFT JOIN user AS owner_user ON candidate.owner = owner_user.user_id', + 'pagerRender' => 'return StringUtility::makeInitialName($rsData[\'ownerFirstName\'], $rsData[\'ownerLastName\'], false, LAST_NAME_MAXLEN);', + 'exportRender' => 'return $rsData[\'ownerFirstName\'] . " " .$rsData[\'ownerLastName\'];', + 'sortableColumn' => 'ownerSort', + 'pagerWidth' => 75, + 'alphaNavigation' => true, + 'filter' => 'CONCAT(owner_user.first_name, owner_user.last_name)'), + + 'Created' => array('select' => 'DATE_FORMAT(candidate.date_created, \'%m-%d-%y\') AS dateCreated', + 'pagerRender' => 'return $rsData[\'dateCreated\'];', + 'sortableColumn' => 'dateCreatedSort', + 'pagerWidth' => 60, + 'filterHaving' => 'DATE_FORMAT(candidate.date_created, \'%m-%d-%y\')'), + + 'Modified' => array('select' => 'DATE_FORMAT(candidate.date_modified, \'%m-%d-%y\') AS dateModified', + 'pagerRender' => 'return $rsData[\'dateModified\'];', + 'sortableColumn' => 'dateModifiedSort', + 'pagerWidth' => 60, + 'pagerOptional' => false, + 'filterHaving' => 'DATE_FORMAT(candidate.date_modified, \'%m-%d-%y\')'), + + /* This one only works when called from the saved list view. Thats why it is not optional, filterable, or exportable. + * FIXME: Somehow make this defined in the associated savedListDataGrid class child. + */ + 'Added To List' => array('select' => 'DATE_FORMAT(saved_list_entry.date_created, \'%m-%d-%y\') AS dateAddedToList, + saved_list_entry.date_created AS dateAddedToListSort', + 'pagerRender' => 'return $rsData[\'dateAddedToList\'];', + 'sortableColumn' => 'dateAddedToListSort', + 'pagerWidth' => 60, + 'pagerOptional' => false, + 'filterable' => false, + 'exportable' => false), + + 'OwnerID' => array('select' => '', + 'filter' => 'candidate.owner', + 'pagerOptional' => false, + 'filterable' => false, + 'filterDescription' => 'Only My Candidates'), + + 'IsHot' => array('select' => '', + 'filter' => 'candidate.is_hot', + 'pagerOptional' => false, + 'filterable' => false, + 'filterDescription' => 'Only Hot Candidates'), + // Tags filtering + 'Tags' => array( + 'select' => '( + SELECT TRIM(GROUP_CONCAT(\' \',t2.title)) FROM candidate_tag t1 + LEFT JOIN tag t2 ON t1.tag_id = t2.tag_id + WHERE t1.candidate_id = candidate.candidate_id + GROUP BY candidate_id + ) as tags + ', + 'sortableColumn' => 'tags', + 'pagerRender' => 'return $rsData[\'tags\'];', + 'pagerOptional' => false, + 'pagerWidth' => 310, + 'exportable' => false, + 'filterable' => false, + + 'filterTypes' => '=#', + 'filterRender=#' => ' + return "candidate.candidate_id IN ( + SELECT t1.candidate_id tags FROM candidate t1 + LEFT JOIN candidate_tag t2 ON t1.candidate_id = t2.candidate_id + WHERE t2.site_id = 1 AND t2.tag_id IN (". implode(",",$arguments)."))"; + ') + ); + + if (US_ZIPS_ENABLED) + { + $this->_classColumns['Near Zipcode'] = + array('select' => 'candidate.zip AS zip', + 'filter' => 'candidate.zip', + 'pagerOptional' => false, + 'filterTypes' => '=@'); + } + + /* Extra fields get added as columns here. */ + $candidates = new Candidates($this->_siteID); + $extraFieldsRS = $candidates->extraFields->getSettings(); + foreach ($extraFieldsRS as $index => $data) + { + $fieldName = $data['fieldName']; + + if (!isset($this->_classColumns[$fieldName])) + { + $columnDefinition = $candidates->extraFields->getDataGridDefinition($index, $data, $this->_db); + + /* Return false for extra fields that should not be columns. */ + if ($columnDefinition !== false) + { + $this->_classColumns[$fieldName] = $columnDefinition; + } + } + } + + parent::__construct($instanceName, $parameters, $misc); + } + + /** + * Returns the sql statment for the pager. + * + * @return array Candidates data + */ + public function getSQL($selectSQL, $joinSQL, $whereSQL, $havingSQL, $orderSQL, $limitSQL, $distinct = '') + { + // FIXME: Factor out Session dependency. + if ($_SESSION['CATS']->isLoggedIn() && $_SESSION['CATS']->getAccessLevel() < ACCESS_LEVEL_MULTI_SA) + { + $adminHiddenCriterion = 'AND candidate.is_admin_hidden = 0'; + } + else + { + $adminHiddenCriterion = ''; + } + + if ($this->getMiscArgument() != 0) + { + $savedListID = (int) $this->getMiscArgument(); + $joinSQL .= ' INNER JOIN saved_list_entry + ON saved_list_entry.data_item_type = '.DATA_ITEM_CANDIDATE.' + AND saved_list_entry.data_item_id = candidate.candidate_id + AND saved_list_entry.site_id = '.$this->_siteID.' + AND saved_list_entry.saved_list_id = '.$savedListID; + } + else + { + $joinSQL .= ' LEFT JOIN saved_list_entry + ON saved_list_entry.data_item_type = '.DATA_ITEM_CANDIDATE.' + AND saved_list_entry.data_item_id = candidate.candidate_id + AND saved_list_entry.site_id = '.$this->_siteID; + } + + $sql = sprintf( + "SELECT SQL_CALC_FOUND_ROWS %s + candidate.candidate_id AS candidateID, + candidate.candidate_id AS exportID, + candidate.is_hot AS isHot, + candidate.date_modified AS dateModifiedSort, + candidate.date_created AS dateCreatedSort, + %s + FROM + candidate + %s + WHERE + candidate.site_id = %s + %s + %s + %s + GROUP BY candidate.candidate_id + %s + %s + %s", + $distinct, + $selectSQL, + $joinSQL, + $this->_siteID, + $adminHiddenCriterion, + (strlen($whereSQL) > 0) ? ' AND ' . $whereSQL : '', + $this->_assignedCriterion, + (strlen($havingSQL) > 0) ? ' HAVING ' . $havingSQL : '', + $orderSQL, + $limitSQL + ); + + return $sql; + } +} + +?> diff --git a/lib/Search.php b/lib/Search.php index 9f6a177d4..6970fafac 100755 --- a/lib/Search.php +++ b/lib/Search.php @@ -375,7 +375,50 @@ public function __construct($siteID) $this->_userID = $_SESSION['CATS']->getUserID(); } - + /** + * Returns all candidates. + * + * @param string wildcard match string + * @return array candidates data + */ + public function all($wildCardString, $sortBy, $sortDirection) + { + + $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.site_id = %s + ORDER BY + %s %s", + $this->_siteID, + $sortBy, + $sortDirection + ); + + return $this->_db->getAllAssoc($sql); + } + /** * Returns all candidates with full names matching $wildCardString. * diff --git a/lib/TemplateUtility.php b/lib/TemplateUtility.php index 667437459..a471ccdde 100755 --- a/lib/TemplateUtility.php +++ b/lib/TemplateUtility.php @@ -1123,9 +1123,18 @@ public static function filterRemoveTextBlock($text, $startBlock, $endBlock, $clo return $text; } - public static function printSingleQuickActionMenu($dataItemType, $dataItemID) + public static function printSingleQuickActionMenu($dataItemType, $dataItemID, $url1 = '', $url2= '' ) { - echo ''; + if($url1 == "" && $url2 == "") + { + echo ''; + } + else + { + $url1 = "'".$url1."'"; + $url2 = "'".$url2."'"; + echo ''; + } } public static function _printQuickActionMenuHolder() diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index 24a469843..31353edeb 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -48,6 +48,8 @@ include_once('./lib/ParseUtility.php'); include_once('./lib/Questionnaire.php'); include_once('./lib/Tags.php'); +include_once('./lib/Search.php'); +include_once('./lib/Duplicates.php'); class CandidatesUI extends UserInterface { @@ -608,6 +610,7 @@ private function show() $this->_template->assign('sessionCookie', $_SESSION['CATS']->getCookie()); $this->_template->assign('tagsRS', $tags->getAll()); $this->_template->assign('assignedTags', $tags->getCandidateTagsTitle($candidateID)); + $this->_template->assign('isDuplicate', 1); if (!eval(Hooks::get('CANDIDATE_SHOW'))) return; @@ -2581,6 +2584,9 @@ private function _addCandidate($isModal, $directoryOverride = '') if (!eval(Hooks::get('CANDIDATE_ON_ADD_PRE'))) return; $candidates = new Candidates($this->_siteID); + + $duplicatesID = $candidates->checkDuplicity($firstName, $middleName, $lastName, $email1, $email2, $phoneHome, $phoneCell, $phoneWork, $address, $city); + $candidateID = $candidates->add( $firstName, $middleName, @@ -2616,6 +2622,12 @@ private function _addCandidate($isModal, $directoryOverride = '') { return $candidateID; } + + $duplicates = new Duplicates($this->_siteID); + if(sizeof($duplicatesID) > 0) + { + $duplicates->addDuplicates($candidateID, $duplicatesID); + } /* Update extra fields. */ $candidates->extraFields->setValuesOnEdit($candidateID); diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index db846c277..cd37b660a 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -432,6 +432,12 @@      + accessLevel >= ACCESS_LEVEL_SA): ?> + + add duplicate Link duplicate + +      +

diff --git a/modules/duplicates/Duplicates.tpl b/modules/duplicates/Duplicates.tpl new file mode 100644 index 000000000..981fa5d44 --- /dev/null +++ b/modules/duplicates/Duplicates.tpl @@ -0,0 +1,158 @@ + + + +active); ?> +dataGrid->getInstanceName());?> + +

+ + +
totalDuplicates ? ' style="background-color: #E6EEFF; padding: 0px;"' : ''; ?>> + totalDuplicates): ?> + + + + + + +
+ Candidates  +

Duplicates: Home

+
+ + + + + + + + + + +
+ dataGrid->printNavigation(false); ?> + + dataGrid->getFilterValue('OwnerID') == $this->userID): ?>checked onclick="dataGrid->getJSAddRemoveFilterFromCheckbox('OwnerID', '==', $this->userID); ?>" /> + Only My Candidates  + + dataGrid->getFilterValue('IsHot') == '1'): ?>checked onclick="dataGrid->getJSAddRemoveFilterFromCheckbox('IsHot', '==', '\'1\''); ?>" /> +   + + Filter by tag +
+
+ + +
Tag list + + +
+ + +
    + + $v){ + if ($v['tag_parent_id'] == $id){ + ?>
  • "; + drw($data, $v['tag_id']); + echo "\n
"; + } + } + } + drw($this->tagsRS, ''); + ?> +
+
+ +
+
+
+ + topLog != ''): ?> +
+ topLog; ?> +
+ + + errMessage != ''): ?> +
+ + + + + +
+ + + There was a problem with your request: +
errMessage; ?>
+
+
+ + +

+ Duplicates - Page dataGrid->getCurrentPageHTML()); ?> (dataGrid->getNumberOfRows()); ?> Items) + + dataGrid->drawRowsPerPageSelector(); ?> + dataGrid->drawShowFilterControl(); ?> +   +

+ + dataGrid->drawFilterArea(); ?> + dataGrid->draw(); ?> + +
+ + dataGrid->printActionArea(); ?> + + + dataGrid->printNavigation(true); ?> +   +
+ + + +



+
+   +
+

+ + + +
+
+ +
+ diff --git a/modules/duplicates/DuplicatesUI.php b/modules/duplicates/DuplicatesUI.php new file mode 100644 index 000000000..bd2429a4b --- /dev/null +++ b/modules/duplicates/DuplicatesUI.php @@ -0,0 +1,694 @@ +_authenticationRequired = true; + $this->_moduleDirectory = 'duplicates'; + $this->_moduleName = 'duplicates'; + $this->_moduleTabText = 'Duplicates'; + } + + + public function handleRequest() + { + if (!eval(Hooks::get('DUPLICATES_HANDLE_REQUEST'))) return; + + $action = $this->getAction(); + switch ($action) + { + case 'show': + $this->show(); + break; + + case 'viewResume': + include_once('./lib/Search.php'); + + $this->viewResume(); + break; + + /* Hot List Page */ + case 'savedLists': + $this->savedList(); + break; + + case 'linkDuplicate': + $this->findDuplicateCandidateSearch(); + break; + + /* Merge two duplicate candidates into the older one */ + case 'merge': + $this->mergeDuplicates(); + break; + + case 'mergeInfo': + $this->mergeDuplicatesInfo(); + break; + + /* Remove duplicity warning from a new candidate */ + case 'removeDuplicity': + $this->removeDuplicity(); + break; + + case 'addDuplicates': + $this->addDuplicates(); + break; + + /* Main candidates page. */ + case 'listByView': + default: + $this->listByView(); + break; + } + } + + + /* + * Called by handleRequest() to process loading the list / main page. + */ + private function listByView($errMessage = '') + { + // Log message that shows up on the top of the list page + $topLog = ''; + + $dataGridProperties = DataGrid::getRecentParamaters("duplicates:duplicatesListByViewDataGrid"); + + /* If this is the first time we visited the datagrid this session, the recent paramaters will + * be empty. Fill in some default values. */ + if ($dataGridProperties == array()) + { + $dataGridProperties = array('rangeStart' => 0, + 'maxResults' => 15, + 'filterVisible' => false); + } + + //$newParameterArray = $this->_parameters; + $tags = new Tags($this->_siteID); + $tagsRS = $tags->getAll(); + //foreach($tagsRS as $r) $r['link'] = DataGrid::_makeControlLink($newParameterArray); + + $dataGrid = DataGrid::get("duplicates:duplicatesListByViewDataGrid", $dataGridProperties); + + $duplicates = new Duplicates($this->_siteID); + $this->_template->assign('totalDuplicates', $duplicates->getCount()); + + $this->_template->assign('active', $this); + $this->_template->assign('dataGrid', $dataGrid); + $this->_template->assign('userID', $_SESSION['CATS']->getUserID()); + $this->_template->assign('errMessage', $errMessage); + $this->_template->assign('topLog', $topLog); + $this->_template->assign('tagsRS', $tagsRS); + + if (!eval(Hooks::get('DUPLICATE_LIST_BY_VIEW'))) return; + + $this->_template->display('./modules/duplicates/Duplicates.tpl'); + } + + /* + * Called by handleRequest() to process loading the details page. + */ + private function show() + { + /* Is this a popup? */ + if (isset($_GET['display']) && $_GET['display'] == 'popup') + { + $isPopup = true; + } + else + { + $isPopup = false; + } + + /* Bail out if we don't have a valid candidate ID. */ + if (!$this->isRequiredIDValid('candidateID', $_GET) && !isset($_GET['email'])) + { + CommonErrors::fatal(COMMONERROR_BADINDEX, $this, 'Invalid candidate ID.'); + } + + $candidates = new Candidates($this->_siteID); + + if (isset($_GET['candidateID'])) + { + $candidateID = $_GET['candidateID']; + } + else + { + $candidateID = $candidates->getIDByEmail($_GET['email']); + } + + $data = $candidates->getWithDuplicity($candidateID); + + /* Bail out if we got an empty result set. */ + if (empty($data)) + { + CommonErrors::fatal(COMMONERROR_BADINDEX, $this, 'The specified candidate ID could not be found.'); + return; + } + + if ($data['isAdminHidden'] == 1 && $this->_accessLevel < ACCESS_LEVEL_MULTI_SA) + { + $this->listByView('This candidate is hidden - only a CATS Administrator can unlock the candidate.'); + return; + } + + /* We want to handle formatting the city and state here instead + * of in the template. + */ + $data['cityAndState'] = StringUtility::makeCityStateString( + $data['city'], $data['state'] + ); + + /* + * Replace newlines with
, fix HTML "special" characters, and + * strip leading empty lines and spaces. + */ + $data['notes'] = trim( + nl2br(htmlspecialchars($data['notes'], ENT_QUOTES)) + ); + + /* Chop $data['notes'] to make $data['shortNotes']. */ + if (strlen($data['notes']) > self::NOTES_MAXLEN) + { + $data['shortNotes'] = substr( + $data['notes'], 0, self::NOTES_MAXLEN + ); + $isShortNotes = true; + } + else + { + $data['shortNotes'] = $data['notes']; + $isShortNotes = false; + } + + /* Format "can relocate" status. */ + if ($data['canRelocate'] == 1) + { + $data['canRelocate'] = 'Yes'; + } + else + { + $data['canRelocate'] = 'No'; + } + + if ($data['isHot'] == 1) + { + $data['titleClass'] = 'jobTitleHot'; + } + else + { + $data['titleClass'] = 'jobTitleCold'; + } + + $attachments = new Attachments($this->_siteID); + $attachmentsRS = $attachments->getAll( + DATA_ITEM_CANDIDATE, $candidateID + ); + + foreach ($attachmentsRS as $rowNumber => $attachmentsData) + { + /* If profile image is not local, force it to be local. */ + if ($attachmentsData['isProfileImage'] == 1) + { + $attachments->forceAttachmentLocal($attachmentsData['attachmentID']); + } + + /* Show an attachment icon based on the document's file type. */ + $attachmentIcon = strtolower( + FileUtility::getAttachmentIcon( + $attachmentsRS[$rowNumber]['originalFilename'] + ) + ); + + $attachmentsRS[$rowNumber]['attachmentIcon'] = $attachmentIcon; + + /* If the text field has any text, show a preview icon. */ + if ($attachmentsRS[$rowNumber]['hasText']) + { + $attachmentsRS[$rowNumber]['previewLink'] = sprintf( + '(Preview)', + CATSUtility::getIndexName(), + $attachmentsRS[$rowNumber]['attachmentID'] + ); + } + else + { + $attachmentsRS[$rowNumber]['previewLink'] = ' '; + } + } + $pipelines = new Pipelines($this->_siteID); + $pipelinesRS = $pipelines->getCandidatePipeline($candidateID); + + $sessionCookie = $_SESSION['CATS']->getCookie(); + + /* Format pipeline data. */ + foreach ($pipelinesRS as $rowIndex => $row) + { + /* Hot jobs [can] have different title styles than normal + * jobs. + */ + if ($row['isHot'] == 1) + { + $pipelinesRS[$rowIndex]['linkClass'] = 'jobLinkHot'; + } + else + { + $pipelinesRS[$rowIndex]['linkClass'] = 'jobLinkCold'; + } + + $pipelinesRS[$rowIndex]['ownerAbbrName'] = StringUtility::makeInitialName( + $pipelinesRS[$rowIndex]['ownerFirstName'], + $pipelinesRS[$rowIndex]['ownerLastName'], + false, + LAST_NAME_MAXLEN + ); + + $pipelinesRS[$rowIndex]['addedByAbbrName'] = StringUtility::makeInitialName( + $pipelinesRS[$rowIndex]['addedByFirstName'], + $pipelinesRS[$rowIndex]['addedByLastName'], + false, + LAST_NAME_MAXLEN + ); + + $pipelinesRS[$rowIndex]['ratingLine'] = TemplateUtility::getRatingObject( + $pipelinesRS[$rowIndex]['ratingValue'], + $pipelinesRS[$rowIndex]['candidateJobOrderID'], + $sessionCookie + ); + } + + $activityEntries = new ActivityEntries($this->_siteID); + $activityRS = $activityEntries->getAllByDataItem($candidateID, DATA_ITEM_CANDIDATE); + if (!empty($activityRS)) + { + foreach ($activityRS as $rowIndex => $row) + { + if (empty($activityRS[$rowIndex]['notes'])) + { + $activityRS[$rowIndex]['notes'] = '(No Notes)'; + } + + if (empty($activityRS[$rowIndex]['jobOrderID']) || + empty($activityRS[$rowIndex]['regarding'])) + { + $activityRS[$rowIndex]['regarding'] = 'General'; + } + + $activityRS[$rowIndex]['enteredByAbbrName'] = StringUtility::makeInitialName( + $activityRS[$rowIndex]['enteredByFirstName'], + $activityRS[$rowIndex]['enteredByLastName'], + false, + LAST_NAME_MAXLEN + ); + } + } + + /* Get upcoming calendar entries. */ + $calendarRS = $candidates->getUpcomingEvents($candidateID); + if (!empty($calendarRS)) + { + foreach ($calendarRS as $rowIndex => $row) + { + $calendarRS[$rowIndex]['enteredByAbbrName'] = StringUtility::makeInitialName( + $calendarRS[$rowIndex]['enteredByFirstName'], + $calendarRS[$rowIndex]['enteredByLastName'], + false, + LAST_NAME_MAXLEN + ); + } + } + + /* Get extra fields. */ + $extraFieldRS = $candidates->extraFields->getValuesForShow($candidateID); + + /* Add an MRU entry. */ + $_SESSION['CATS']->getMRU()->addEntry( + DATA_ITEM_CANDIDATE, $candidateID, $data['firstName'] . ' ' . $data['lastName'] + ); + + /* Is the user an admin - can user see history? */ + if ($this->_accessLevel < ACCESS_LEVEL_DEMO) + { + $privledgedUser = false; + } + else + { + $privledgedUser = true; + } + + $EEOSettings = new EEOSettings($this->_siteID); + $EEOSettingsRS = $EEOSettings->getAll(); + $EEOValues = array(); + + /* Make a list of all EEO related values so they can be positioned by index + * rather than static positioning (like extra fields). */ + if ($EEOSettingsRS['enabled'] == 1) + { + if ($EEOSettingsRS['genderTracking'] == 1) + { + $EEOValues[] = array('fieldName' => 'Gender', 'fieldValue' => $data['eeoGenderText']); + } + if ($EEOSettingsRS['ethnicTracking'] == 1) + { + $EEOValues[] = array('fieldName' => 'Ethnicity', 'fieldValue' => $data['eeoEthnicType']); + } + if ($EEOSettingsRS['veteranTracking'] == 1) + { + $EEOValues[] = array('fieldName' => 'Veteran Status', 'fieldValue' => $data['eeoVeteranType']); + } + if ($EEOSettingsRS['disabilityTracking'] == 1) + { + $EEOValues[] = array('fieldName' => 'Disability Status', 'fieldValue' => $data['eeoDisabilityStatus']); + } + } + + $tags = new Tags($this->_siteID); + + $questionnaire = new Questionnaire($this->_siteID); + $questionnaires = $questionnaire->getCandidateQuestionnaires($candidateID); + + $this->_template->assign('active', $this); + $this->_template->assign('questionnaires', $questionnaires); + $this->_template->assign('data', $data); + $this->_template->assign('isShortNotes', $isShortNotes); + $this->_template->assign('attachmentsRS', $attachmentsRS); + $this->_template->assign('pipelinesRS', $pipelinesRS); + $this->_template->assign('activityRS', $activityRS); + $this->_template->assign('calendarRS', $calendarRS); + $this->_template->assign('extraFieldRS', $extraFieldRS); + $this->_template->assign('candidateID', $candidateID); + $this->_template->assign('isPopup', $isPopup); + $this->_template->assign('EEOSettingsRS', $EEOSettingsRS); + $this->_template->assign('EEOValues', $EEOValues); + $this->_template->assign('privledgedUser', $privledgedUser); + $this->_template->assign('sessionCookie', $_SESSION['CATS']->getCookie()); + $this->_template->assign('tagsRS', $tags->getAll()); + $this->_template->assign('assignedTags', $tags->getCandidateTagsTitle($candidateID)); + + if (!eval(Hooks::get('DUPLICATES_SHOW'))) return; + + $this->_template->display('./modules/duplicates/Show.tpl'); + } + + + + + /* + * Called by handleRequest() to process showing a resume preview. + */ + private function viewResume() + { + /* Bail out if we don't have a valid candidate ID. */ + if (!$this->isRequiredIDValid('attachmentID', $_GET)) + { + CommonErrors::fatal(COMMONERROR_BADINDEX, $this, 'Invalid attachment ID.'); + } + + $attachmentID = $_GET['attachmentID']; + + /* Get the search string. */ + $query = $this->getTrimmedInput('wildCardString', $_GET); + + /* Get resume text. */ + $candidates = new Candidates($this->_siteID); + $data = $candidates->getResume($attachmentID); + + if (!empty($data)) + { + /* Keyword highlighting. */ + $data['text'] = SearchUtility::makePreview($query, $data['text']); + } + + if (!eval(Hooks::get('CANDIDATE_VIEW_RESUME'))) return; + + $this->_template->assign('active', $this); + $this->_template->assign('data', $data); + $this->_template->display('./modules/candidates/ResumeView.tpl'); + } + + + + /** + * Formats SQL result set for display. This is factored out for code + * clarity. + * + * @param array result set from listByView() + * @return array formatted result set + */ + private function _formatListByViewResults($resultSet) + { + if (empty($resultSet)) + { + return $resultSet; + } + + foreach ($resultSet as $rowIndex => $row) + { + if ($resultSet[$rowIndex]['isHot'] == 1) + { + $resultSet[$rowIndex]['linkClass'] = 'jobLinkHot'; + } + else + { + $resultSet[$rowIndex]['linkClass'] = 'jobLinkCold'; + } + + if (!empty($resultSet[$rowIndex]['ownerFirstName'])) + { + $resultSet[$rowIndex]['ownerAbbrName'] = StringUtility::makeInitialName( + $resultSet[$rowIndex]['ownerFirstName'], + $resultSet[$rowIndex]['ownerLastName'], + false, + LAST_NAME_MAXLEN + ); + } + else + { + $resultSet[$rowIndex]['ownerAbbrName'] = 'None'; + } + + if ($resultSet[$rowIndex]['submitted'] == 1) + { + $resultSet[$rowIndex]['iconTag'] = ''; + } + else + { + $resultSet[$rowIndex]['iconTag'] = ''; + } + + if ($resultSet[$rowIndex]['attachmentPresent'] == 1) + { + $resultSet[$rowIndex]['iconTag'] .= ''; + } + else + { + $resultSet[$rowIndex]['iconTag'] .= ''; + } + + + if (empty($resultSet[$rowIndex]['keySkills'])) + { + $resultSet[$rowIndex]['keySkills'] = ' '; + } + else + { + $resultSet[$rowIndex]['keySkills'] = htmlspecialchars( + $resultSet[$rowIndex]['keySkills'] + ); + } + + /* Truncate Key Skills to fit the column width */ + if (strlen($resultSet[$rowIndex]['keySkills']) > self::TRUNCATE_KEYSKILLS) + { + $resultSet[$rowIndex]['keySkills'] = substr( + $resultSet[$rowIndex]['keySkills'], + 0, + self::TRUNCATE_KEYSKILLS + ) . "..."; + } + } + + return $resultSet; + } + + /* + * Called by handleRequest() to handle processing an "Add to a Job Order + * Pipeline" search and displaying the results in the modal dialog, or + * to show the initial dialog. + */ + private function findDuplicateCandidateSearch() + { + $duplicateCandidateID = $_GET['candidateID']; + if($duplicateCandidateID == "") + { + $duplicateCandidateID = $_POST['candidateID']; + } + $query = $this->getTrimmedInput('wildCardString', $_POST); + $mode = $this->getTrimmedInput('mode', $_POST); + + /* Execute the search. */ + $search = new SearchCandidates($this->_siteID); + switch ($mode) + { + case 'searchByCandidateName': + $rs = $search->byFullName($query, 'candidate.last_name', 'ASC', true); + $resultsMode = true; + break; + + default: + $rs = $search->all($query, 'candidate.last_name', 'ASC', 'true'); + $resultsMode = false; + break; + } + + $duplicates = new Duplicates($this->_siteID); + + foreach ($rs as $rowIndex => $row) + { + $rs[$rowIndex]['duplicateCandidateID'] = $duplicateCandidateID; + if ($duplicates->checkIfLinked($rs[$rowIndex]['candidateID'], $duplicateCandidateID)) + { + $rs[$rowIndex]['linked'] = true; + } + else + { + $rs[$rowIndex]['linked'] = false; + } + + if ($row['isHot'] == 1) + { + $rs[$rowIndex]['linkClass'] = 'jobLinkHot'; + } + else + { + $rs[$rowIndex]['linkClass'] = 'jobLinkCold'; + } + } + + if (!eval(Hooks::get('DUPLICATE_ON_LINK_DUPLICATES'))) return; + + $this->_template->assign('rs', $rs); + $this->_template->assign('isFinishedMode', false); + $this->_template->assign('isResultsMode', $resultsMode); + $this->_template->assign('duplicateCandidateID', $duplicateCandidateID); + $this->_template->display('./modules/duplicates/LinkDuplicity.tpl'); + } + + private function mergeDuplicates() + { + $duplicates = new Duplicates($this->_siteID); + $candidates = new Candidates($this->_siteID); + $oldCandidateID = $_GET['oldCandidateID']; + $newCandidateID = $_GET['newCandidateID']; + + $rsOld = $candidates->getWithDuplicity($oldCandidateID); + $rsNew = $candidates->getWithDuplicity($newCandidateID); + + $this->_template->assign('isFinishedMode', false); + $this->_template->assign('rsOld', $rsOld); + $this->_template->assign('rsNew', $rsNew); + $this->_template->assign('oldCandidateID', $oldCandidateID); + $this->_template->assign('newCandidateID', $newCandidateID); + $this->_template->display('./modules/duplicates/Merge.tpl'); + } + + private function mergeDuplicatesInfo() + { + $duplicates = new Duplicates($this->_siteID); + $candidates = new Candidates($this->_siteID); + $params = array(); + $params['firstName'] = $_POST['firstName']; + $params['middleName'] = $_POST['middleName']; + $params['lastName'] = $_POST['lastName']; + if(isset($_POST['email'])) + { + $params['emails'] = $_POST['email']; + } + else + { + $params['emails'] = array(); + } + $params['phoneCell'] = $_POST['phoneCell']; + $params['phoneWork'] = $_POST['phoneWork']; + $params['phoneHome'] = $_POST['phoneHome']; + $params['address'] = $_POST['address']; + $params['website'] = $_POST['website']; + $params['oldCandidateID'] = $_POST['oldCandidateID']; + $params['newCandidateID'] = $_POST['newCandidateID']; + + $duplicates->mergeDuplicates($params, $candidates->getWithDuplicity($params['newCandidateID'])); + $this->_template->assign('isFinishedMode', true); + $this->_template->display('./modules/duplicates/Merge.tpl'); + } + + private function removeDuplicity() + { + $duplicates = new Duplicates($this->_siteID); + $oldCandidateID = $_GET['oldCandidateID']; + $newCandidateID = $_GET['newCandidateID']; + $duplicates->removeDuplicity($oldCandidateID, $newCandidateID); + $url = CATSUtility::getIndexName()."?m=duplicates"; + header("Location: " . $url); /* Redirect browser */ + exit(); + } + + + private function addDuplicates() + { + $duplicates = new Duplicates($this->_siteID); + $oldCandidateID = $_GET['candidateID']; + $newCandidateID = $_GET['duplicateCandidateID']; + $duplicates->addDuplicates($newCandidateID, $oldCandidateID); + $this->_template->assign('isFinishedMode', true); + $this->_template->display('./modules/duplicates/LinkDuplicity.tpl'); + } +} + +?> diff --git a/modules/duplicates/Error.tpl b/modules/duplicates/Error.tpl new file mode 100644 index 000000000..5da7f6eeb --- /dev/null +++ b/modules/duplicates/Error.tpl @@ -0,0 +1,37 @@ + + + +active); ?> +
+ + +
+ + + + + +
+ Contacts  +

Duplicates: Error

+ +

+ A fatal error has occurred.
+
+ errorMessage); ?> +

+
+
+
+ diff --git a/modules/duplicates/LinkDuplicity.tpl b/modules/duplicates/LinkDuplicity.tpl new file mode 100644 index 000000000..f1f571d36 --- /dev/null +++ b/modules/duplicates/LinkDuplicity.tpl @@ -0,0 +1,161 @@ + + + + isFinishedMode): ?> +

Search for a candidate below, and then click on the candidate name to link + this candidate as a duplicate to them.

+ + + + + + duplicateCandidateID)?> /> + + + + + + + + + + + + + +
Search by Candidate Name:  *
 
+ + + + + + isResultsMode): ?> +
+

Search Results

+ + rs)): ?> + + + + + + + + + + + + + rs as $rowNumber => $data): ?> + + + + + + + + + + + +
First NameLast NameE-mailCell phoneCityStateOwnerAction
+ + + _($data['firstName']); ?> + + + _($data['firstName']); ?> + + + + + _($data['lastName']); ?> + + + _($data['lastName']); ?> + + _($data['email1']); ?>_($data['phoneCell']); ?>_($data['city']); ?>_($data['state']); ?>_($data['ownerFirstName'])." ".$this->_($data['ownerLastName']); ?> + + consider + +
+ +

No matching entries found.

+ + +
+

All Candidates

+ + rs)): ?> + + + + + + + + + + + + + rs as $rowNumber => $data): ?> + + + + + + + + + + + +
First NameLast NameE-mailCell phoneCityStateOwnerAction
+ + + _($data['firstName']); ?> + + + _($data['firstName']); ?> + + + + + _($data['lastName']); ?> + + + _($data['lastName']); ?> + + _($data['email1']); ?>_($data['phoneCell']); ?>_($data['city']); ?>_($data['state']); ?>_($data['ownerFirstName'])." ".$this->_($data['ownerLastName']); ?> + + consider + +
+ +

No candidates found.

+ + + +

This candidate has been successfully added as a duplicate for the selected candidate.

+ +
+ +
+ + + + diff --git a/modules/duplicates/Merge.tpl b/modules/duplicates/Merge.tpl new file mode 100644 index 000000000..7454e0f4c --- /dev/null +++ b/modules/duplicates/Merge.tpl @@ -0,0 +1,175 @@ + + + + isFinishedMode): ?> + + + + oldCandidateID; ?> /> + newCandidateID; ?> /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rsNew['email1'] != "" || $this->rsOld['email1'] != ""): ?> + + + + + + + + rsNew['email2'] != "" || $this->rsOld['email2'] != ""): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rsOld['address'] == "" && $this->rsOld['city'] == "" && $this->rsOld['state'] == "" && $this->rsOld['zip'] == ""): ?> + + + + + + + rsNew['address'] == "" && $this->rsNew['city'] == "" && $this->rsNew['state'] == "" && $this->rsNew['zip'] == ""): ?> + + + + + + + + + + + + + + +
Original candidate Duplicate candidate 
First Name 
rsOld['firstName']); ?>rsNew['firstName']); ?>
Middle Name 
rsOld['middleName']); ?>rsNew['middleName']); ?>
Last Name 
rsOld['lastName']); ?>rsNew['lastName']); ?>
E-mails (max. 2) 
rsOld['email1'] == '') ? '(none)' : ($this->rsOld['email1']); ?>rsNew['email1'] == '') ? '(none)' : ($this->rsNew['email1']); ?>
rsOld['email2'] == '') ? '(none)' : ($this->rsOld['email2']); ?>rsNew['email2'] == '') ? '(none)' : ($this->rsNew['email2']); ?>
Cell phone 
rsOld['phoneCell'] == '') ? '(none)' : ($this->rsOld['phoneCell']); ?>rsNew['phoneCell'] == '') ? '(none)' : ($this->rsNew['phoneCell']); ?>
Home phone 
rsOld['phoneHome'] == '') ? '(none)' : ($this->rsOld['phoneHome']); ?>rsNew['phoneHome'] == '') ? '(none)' : ($this->rsNew['phoneHome']); ?>
Work phone 
rsOld['phoneWork'] == '') ? '(none)' : ($this->rsOld['phoneWork']); ?>rsNew['phoneWork'] == '') ? '(none)' : ($this->rsNew['phoneWork']); ?>
Website 
rsOld['webSite'] == '') ? '(none)' : ($this->rsOld['webSite']); ?>rsNew['webSite'] == '') ? '(none)' : ($this->rsNew['webSite']); ?>
Address 
rsOld['address'].'
'.$this->rsOld['city']." ".$this->rsOld['zip'].'
'.$this->rsOld['state']); ?>
rsNew['address'].'
'.$this->rsNew['city']." ".$this->rsNew['zip'].'
'.$this->rsNew['state']); ?>
 
+ +

These candidates have been successfully merged.

+ +
+ +
+ + + + + diff --git a/modules/duplicates/Show.tpl b/modules/duplicates/Show.tpl new file mode 100644 index 000000000..b226c50bb --- /dev/null +++ b/modules/duplicates/Show.tpl @@ -0,0 +1,547 @@ + +isPopup): ?> + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js')); ?> + + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js')); ?> + + + active); ?> +
+ + + +
+ + + + + +
+ Candidates  +

Duplicates: Duplicate Details + data['isDuplicate'])): ?> + duplicate_warning + data['isDuplicate'] as $item): ?> + Duplicate' ?> + data['candidateID'], + urlencode(CATSUtility::getIndexName().'?m=duplicates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), + urlencode(CATSUtility::getIndexName().'?m=duplicates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'])); ?> + + +

+ +

Duplicate Details

+ + data['isAdminHidden'] == 1): ?> +

This Candidate is hidden. Only CATS Administrators can view it or search for it. To make it visible by the site users, click Here.

+ + + + + + attachmentsRS as $rowNumber => $attachmentsData): ?> + + + + + + + + + + attachmentsRS as $rowNumber => $attachmentsData): ?> + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + extraFieldRS)/2); $i++): ?> + + + + + + + + + + +
Name: + + + _($this->data['firstName']); ?> + _($this->data['middleName']); ?> + _($this->data['lastName']); ?> + data['isActive'] != 1): ?> +  (INACTIVE) + + +
E-Mail: + + _($this->data['email1']); ?> + +
2nd E-Mail: + + _($this->data['email2']); ?> + +
Home Phone:_($this->data['phoneHome']); ?>
Cell Phone:_($this->data['phoneCell']); ?>
Work Phone:_($this->data['phoneWork']); ?>
Best Time To Call:_($this->data['bestTimeToCall']); ?>
Address:data['address']))); ?>
  + _($this->data['cityAndState']); ?> + _($this->data['zip']); ?> +
Web Site: + data['webSite'])): ?> + _($this->data['webSite']); ?> + +
Source:_($this->data['source']); ?>
_($this->extraFieldRS[$i]['fieldName']); ?>:extraFieldRS[$i]['display']); ?>
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + extraFieldRS))/2); $i < (count($this->extraFieldRS)); $i++): ?> + + + + + +
Date Available:_($this->data['dateAvailable']); ?>
Current Employer:_($this->data['currentEmployer']); ?>
Key Skills:_($this->data['keySkills']); ?>
Can Relocate:_($this->data['canRelocate']); ?>
Current Pay:_($this->data['currentPay']); ?>
Desired Pay:_($this->data['desiredPay']); ?>
Pipeline:_($this->data['pipeline']); ?>
Submitted:_($this->data['submitted']); ?>
Created:_($this->data['dateCreated']); ?> (_($this->data['enteredByFullName']); ?>)
Owner:_($this->data['ownerFullName']); ?>
_($this->extraFieldRS[$i]['fieldName']); ?>:extraFieldRS[$i]['display']); ?>
+
+ + + + + + + +
+ isPopup): ?> + accessLevel >= ACCESS_LEVEL_DELETE): ?> + + + + + +      +    + Picture:       +
+ + + +
+
+ + EEOSettingsRS['enabled'] == 1): ?> + + + + + + +
+ + EEOValues)/2); $i++): ?> + + + EEOSettingsRS['canSeeEEOInfo']): ?> + + + + + + +
_($this->EEOValues[$i]['fieldName']); ?>:_($this->EEOValues[$i]['fieldValue']); ?>(Hidden)
+
+ + + + + EEOValues))/2); $i < intval(count($this->EEOValues)); $i++): ?> + + + EEOSettingsRS['canSeeEEOInfo']): ?> + + + + + + +
_($this->EEOValues[$i]['fieldName']); ?>:_($this->EEOValues[$i]['fieldValue']); ?>(Hidden)
+
+ + + + + + +
+ + + + isShortNotes): ?> + + + + + + + + + + + + + questionnaires) && !empty($this->questionnaires)): ?> + + + + + + + + + + + + + + +
Misc. Notes: + data['shortNotes']); ?>...  +

[More]

+
+ data['notes']); ?> +
Upcoming Events: + calendarRS as $rowNumber => $calendarData): ?> + + + +
Questionnaires: + + + + + + + questionnaires as $questionnaire): ?> + + + + + + + +
Title (Internal)CompletedDescription (Public)
+ + view View + +   + + print Print + +
+
Attachments: + + attachmentsRS as $rowNumber => $attachmentsData): ?> + + + + + + + + +
+ + +   + _($attachmentsData['originalFilename']) ?> + + _($attachmentsData['dateCreated']) ?>
+
Tags: + + assignedTags) ?> +
+
+isPopup): ?> + privledgedUser): ?> + +  View History + +      + + +
+
+ +

Job Order Pipeline

+ + + + + + + + + + +isPopup): ?> + + + + + pipelinesRS as $rowNumber => $pipelinesData): ?> + + + + + + + + + +isPopup): ?> + + + + + + + + +
MatchTitleCompanyOwnerAddedEntered ByStatusAction
+ + + + + + + + + + + _($pipelinesData['title']) ?> + + + + _($pipelinesData['companyName']) ?> + + _($pipelinesData['ownerAbbrName']) ?>_($pipelinesData['dateCreated']) ?>_($pipelinesData['addedByAbbrName']) ?>_($pipelinesData['status']) ?> + + getAccessLevel() >= ACCESS_LEVEL_EDIT && !$_SESSION['CATS']->hasUserCategory('sourcer')): ?> + + + + + + + + + accessLevel >= ACCESS_LEVEL_EDIT): ?> + + + + + accessLevel >= ACCESS_LEVEL_DELETE): ?> + + + + +
+ +
+
+ +

Activity

+ + + + + + + + +isPopup): ?> + + + + + activityRS as $rowNumber => $activityData): ?> + + + + + + +isPopup): ?> + + + + +
DateTypeEnteredRegardingNotesAction
_($activityData['dateCreated']) ?>_($activityData['typeDescription']) ?>_($activityData['enteredByAbbrName']) ?>_($activityData['regarding']) ?> + accessLevel >= ACCESS_LEVEL_EDIT): ?> + + + + + accessLevel >= ACCESS_LEVEL_DELETE): ?> + + + + +
+isPopup): ?> +
+ +
+
+
+ + + + + +
+ diff --git a/modules/duplicates/dataGrids.php b/modules/duplicates/dataGrids.php new file mode 100644 index 000000000..a69fa143e --- /dev/null +++ b/modules/duplicates/dataGrids.php @@ -0,0 +1,141 @@ +_tableWidth = new Width(100, '%'); + $this->_defaultAlphabeticalSortBy = 'lastName'; + $this->ajaxMode = false; + $this->showExportCheckboxes = true; //BOXES WILL NOT APPEAR UNLESS SQL ROW exportID IS RETURNED! + $this->showActionArea = true; + $this->showChooseColumnsBox = true; + $this->allowResizing = true; + + $this->defaultSortBy = 'dateModifiedSort'; + $this->defaultSortDirection = 'DESC'; + + $this->_defaultColumns = array( + array('name' => 'Attachments', 'width' => 31), + array('name' => 'First Name', 'width' => 75), + array('name' => 'Last Name', 'width' => 85), + array('name' => 'City', 'width' => 75), + array('name' => 'State', 'width' => 50), + array('name' => 'Key Skills', 'width' => 215), + array('name' => 'Owner', 'width' => 65), + array('name' => 'Created', 'width' => 60), + array('name' => 'Modified', 'width' => 60), + ); + + parent::__construct("duplicates:duplicatesListByViewDataGrid", + $siteID, $parameters, $misc + ); + } + + + /** + * Adds more options to the action area on the pager. Overloads + * DataGrid Inner Action Area function. + * + * @return html innerActionArea commands. + */ + public function getInnerActionArea() + { + //TODO: Add items: + // - Add to List + // - Add to Pipeline + // - Mass set rank (depends on each candidate having their own personal rank - are we going to do this?) + $html = ''; + + $html .= $this->getInnerActionAreaItemPopup('Add To List', CATSUtility::getIndexName().'?m=lists&a=addToListFromDatagridModal&dataItemType='.DATA_ITEM_CANDIDATE, 450, 350); + $html .= $this->getInnerActionAreaItemPopup('Add To Pipeline', CATSUtility::getIndexName().'?m=candidates&a=considerForJobSearch', 750, 460); + if(MAIL_MAILER != 0) + { + $html .= $this->getInnerActionAreaItem('Send E-Mail', CATSUtility::getIndexName().'?m=candidates&a=emailCandidates'); + } + $html .= $this->getInnerActionAreaItem('Export', CATSUtility::getIndexName().'?m=export&a=exportByDataGrid'); + + $html .= parent::getInnerActionArea(); + + return $html; + } +} + +class duplicatesSavedListByViewDataGrid extends DuplicatesDataGrid +{ + public function __construct($siteID, $parameters, $misc) + { + $this->_tableWidth = new Width(100, '%');; + $this->_defaultAlphabeticalSortBy = 'lastName'; + $this->ajaxMode = false; + $this->showExportCheckboxes = true; //BOXES WILL NOT APPEAR UNLESS SQL ROW exportID IS RETURNED! + $this->showActionArea = true; + $this->showChooseColumnsBox = true; + $this->allowResizing = true; + + $this->defaultSortBy = 'dateModifiedSort'; + $this->defaultSortDirection = 'DESC'; + + $this->_defaultColumns = array( + array('name' => 'Attachments', 'width' => 31), + array('name' => 'First Name', 'width' => 75), + array('name' => 'Last Name', 'width' => 85), + array('name' => 'City', 'width' => 75), + array('name' => 'State', 'width' => 50), + array('name' => 'Key Skills', 'width' => 200), + array('name' => 'Owner', 'width' => 65), + array('name' => 'Modified', 'width' => 60), + array('name' => 'Added To List', 'width' => 75), + ); + + parent::__construct("duplicates:duplicatesSavedListByViewDataGrid", + $siteID, $parameters, $misc + ); + } + + /** + * Adds more options to the action area on the pager. Overloads + * DataGrid Inner Action Area function. + * + * @return html innerActionArea commands. + */ + public function getInnerActionArea() + { + //TODO: Add items: + // - Add to List + // - Add to Pipeline + // - Mass set rank (depends on each candidate having their own personal rank - are we going to do this?) + $html = ''; + + $html .= $this->getInnerActionAreaItem('Remove From This List', CATSUtility::getIndexName().'?m=lists&a=removeFromListDatagrid&dataItemType='.DATA_ITEM_CANDIDATE.'&savedListID='.$this->getMiscArgument(), false); + $html .= $this->getInnerActionAreaItemPopup('Add To Pipeline', CATSUtility::getIndexName().'?m=candidates&a=considerForJobSearch', 750, 460); + if(MAIL_MAILER != 0) + { + $html .= $this->getInnerActionAreaItem('Send E-Mail', CATSUtility::getIndexName().'?m=candidates&a=emailCandidates'); + } + $html .= $this->getInnerActionAreaItem('Export', CATSUtility::getIndexName().'?m=export&a=exportByDataGrid'); + + $html .= parent::getInnerActionArea(); + + return $html; + } +} + + +?> \ No newline at end of file From 6b37f0a1811c44591a2f5e33c5eceb5fb0a2e605 Mon Sep 17 00:00:00 2001 From: skrchnavy Date: Tue, 29 Nov 2016 19:52:16 +0100 Subject: [PATCH 03/92] corrected header of file to follow license --- lib/Duplicates.php | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/lib/Duplicates.php b/lib/Duplicates.php index 4ca333dc3..0fdee4f89 100644 --- a/lib/Duplicates.php +++ b/lib/Duplicates.php @@ -1,34 +1,14 @@ Date: Mon, 5 Dec 2016 11:53:04 +0100 Subject: [PATCH 04/92] duplicates module integrated into candidates module and added access level checks for duplicate functions --- lib/Candidates.php | 575 ++++++-- lib/DataGrid.php | 4 +- lib/Duplicates.php | 1253 ----------------- modules/candidates/CandidatesUI.php | 256 +++- .../{duplicates => candidates}/Duplicates.tpl | 8 +- .../LinkDuplicity.tpl | 16 +- modules/{duplicates => candidates}/Merge.tpl | 13 +- modules/candidates/Show.tpl | 2 +- .../Show.tpl => candidates/ShowDuplicate.tpl} | 8 +- modules/candidates/dataGrids.php | 4 +- modules/duplicates/DuplicatesUI.php | 694 --------- modules/duplicates/Error.tpl | 37 - modules/duplicates/dataGrids.php | 141 -- 13 files changed, 742 insertions(+), 2269 deletions(-) delete mode 100644 lib/Duplicates.php rename modules/{duplicates => candidates}/Duplicates.tpl (96%) rename modules/{duplicates => candidates}/LinkDuplicity.tpl (95%) rename modules/{duplicates => candidates}/Merge.tpl (94%) rename modules/{duplicates/Show.tpl => candidates/ShowDuplicate.tpl} (99%) delete mode 100644 modules/duplicates/DuplicatesUI.php delete mode 100644 modules/duplicates/Error.tpl delete mode 100644 modules/duplicates/dataGrids.php diff --git a/lib/Candidates.php b/lib/Candidates.php index a3759de29..ac6d69555 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -559,103 +559,7 @@ public function get($candidateID) public function getWithDuplicity($candidateID) { - $sql = sprintf( - "SELECT - candidate.candidate_id AS candidateID, - candidate.is_active AS isActive, - candidate.first_name AS firstName, - candidate.middle_name AS middleName, - candidate.last_name AS lastName, - candidate.email1 AS email1, - candidate.email2 AS email2, - candidate.phone_home AS phoneHome, - candidate.phone_work AS phoneWork, - candidate.phone_cell AS phoneCell, - candidate.address AS address, - candidate.city AS city, - candidate.state AS state, - candidate.zip AS zip, - candidate.source AS source, - candidate.key_skills AS keySkills, - candidate.current_employer AS currentEmployer, - candidate.current_pay AS currentPay, - candidate.desired_pay AS desiredPay, - candidate.notes AS notes, - candidate.owner AS owner, - candidate.can_relocate AS canRelocate, - candidate.web_site AS webSite, - candidate.best_time_to_call AS bestTimeToCall, - candidate.is_hot AS isHot, - candidate.is_admin_hidden AS isAdminHidden, - DATE_FORMAT( - candidate.date_created, '%%m-%%d-%%y (%%h:%%i %%p)' - ) AS dateCreated, - DATE_FORMAT( - candidate.date_modified, '%%m-%%d-%%y (%%h:%%i %%p)' - ) AS dateModified, - COUNT( - candidate_joborder.joborder_id - ) AS pipeline, - ( - SELECT - COUNT(*) - FROM - candidate_joborder_status_history - WHERE - candidate_id = %s - AND - status_to = %s - AND - site_id = %s - ) AS submitted, - CONCAT( - candidate.first_name, ' ', candidate.last_name - ) AS candidateFullName, - CONCAT( - entered_by_user.first_name, ' ', entered_by_user.last_name - ) AS enteredByFullName, - CONCAT( - owner_user.first_name, ' ', owner_user.last_name - ) AS ownerFullName, - owner_user.email AS owner_email, - DATE_FORMAT( - candidate.date_available, '%%m-%%d-%%y' - ) AS dateAvailable, - eeo_ethnic_type.type AS eeoEthnicType, - eeo_veteran_type.type AS eeoVeteranType, - candidate.eeo_disability_status AS eeoDisabilityStatus, - candidate.eeo_gender AS eeoGender, - IF (candidate.eeo_gender = 'm', - 'Male', - IF (candidate.eeo_gender = 'f', - 'Female', - '')) - AS eeoGenderText - FROM - candidate - LEFT JOIN user AS entered_by_user - ON candidate.entered_by = entered_by_user.user_id - LEFT JOIN user AS owner_user - ON candidate.owner = owner_user.user_id - LEFT JOIN candidate_joborder - ON candidate.candidate_id = candidate_joborder.candidate_id - LEFT JOIN eeo_ethnic_type - ON eeo_ethnic_type.eeo_ethnic_type_id = candidate.eeo_ethnic_type_id - LEFT JOIN eeo_veteran_type - ON eeo_veteran_type.eeo_veteran_type_id = candidate.eeo_veteran_type_id - WHERE - candidate.candidate_id = %s - AND - candidate.site_id = %s - GROUP BY - candidate.candidate_id", - $this->_db->makeQueryInteger($candidateID), - PIPELINE_STATUS_SUBMITTED, - $this->_siteID, - $this->_db->makeQueryInteger($candidateID), - $this->_siteID - ); - $data = $this->_db->getAssoc($sql); + $data = $this->get($candidateID); $sql = sprintf( "SELECT @@ -1304,6 +1208,464 @@ public function checkDuplicity($firstName, $middleName, $lastName, $email1, $ema return $duplicatesID; } } + + /** + * Returns the number of duplicates in the system. + * + * @return integer Number of Duplicates in site. + */ + public function getDuplicatesCount() + { + $sql = sprintf( + "SELECT + COUNT(*) AS totalDuplicates + FROM + (SELECT * + FROM candidate_duplicates + WHERE + candidate_duplicates.site_id = %s + GROUP BY + candidate_duplicates.new_candidate_id) as innerQuery", + $this->_siteID + ); + return $this->_db->getColumn($sql, 0, 0); + } + + /** + * Removes a candidate's duplicate warning/link from the system. + * + * @param integer first Candidate ID. + * @param integer second Candidate ID. + * @return void + */ + public function deleteDuplicate($firstCandidateID, $secondCandidateID) + { + /* Delete the duplicate from candidate_duplicates. */ + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + old_candidate_id = %s + AND + new_candidate_id = %s + OR + new_candidate_id = %s + AND + old_candidate_id = %s" + , + $this->_db->makeQueryInteger($firstCandidateID), + $this->_db->makeQueryInteger($secondCandidateID), + $this->_db->makeQueryInteger($firstCandidateID), + $this->_db->makeQueryInteger($secondCandidateID) + ); + $this->_db->query($sql); + } + + public function removeDuplicity($oldCandidateID, $newCandidateID) + { + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + candidate_duplicates.old_candidate_id = %s + AND + candidate_duplicates.new_candidate_id = %s", + $this->_db->makeQueryStringOrNULL($oldCandidateID), + $this->_db->makeQueryStringOrNULL($newCandidateID) + ); + $this->_db->query($sql); + } + + /** + * Adds a duplicate to the database. + * + * @param string first candidate ID. + * @param string second candidate ID. + * @return integer 1 on success, or -1 on failure. + */ + + public function addDuplicates($candidateID, $duplicates) + { + if(is_array($duplicates)) + { + foreach($duplicates as $duplicateID) + { + $sql = sprintf( + "INSERT INTO candidate_duplicates ( + old_candidate_id, + new_candidate_id, + site_id + ) + VALUES ( + %s, + %s, + %s + )", + $this->_db->makeQueryString($duplicateID), + $this->_db->makeQueryString($candidateID), + $this->_siteID + ); + $this->_db->query($sql); + } + } + else if($duplicates != "") + { + $sql = sprintf( + "INSERT INTO candidate_duplicates ( + old_candidate_id, + new_candidate_id, + site_id + ) + VALUES ( + %s, + %s, + %s + )", + $this->_db->makeQueryString($duplicates), + $this->_db->makeQueryString($candidateID), + $this->_siteID + ); + $this->_db->query($sql); + } + } + + + + public function mergeDuplicates($params, $rs) + { + $oldCandidateID = $params['oldCandidateID']; + $newCandidateID = $params['newCandidateID']; + $sql = sprintf( + "UPDATE + activity + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + attachment + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + calendar_event + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + new_candidate_id = %s", + $this->_db->makeQueryInteger($newCandidateID) + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_duplicates + SET + old_candidate_id = %s + WHERE + old_candidate_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID) + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_joborder + SET + candidate_id = %s + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_joborder_status_history + SET + candidate_id = %s + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + candidate_tag + SET + candidate_id = %s + WHERE + candidate_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + $sql = sprintf( + "UPDATE + saved_list_entry + SET + data_item_id = %s + WHERE + data_item_id = %s + AND + site_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_siteID + ); + + $this->_db->query($sql); + + + $update = " "; + $comma = false; + + if($params['firstName'] == "1") + { + $update .= "first_name = '" . $rs['firstName']."'"; + $comma = true; + } + if($params['middleName'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "middle_name = '" . $rs['middleName']."'"; + $comma = true; + } + if($params['lastName'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "last_name = '" . $rs['lastName']."'"; + $comma = true; + } + if($params['phoneCell'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "phone_cell = '" . $rs['phoneCell']."'"; + $comma = true; + } + if($params['phoneWork'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "phone_work = '" . $rs['phoneWork']."'"; + $comma = true; + } + if($params['phoneHome'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "phone_home = '" . $rs['phoneHome']."'"; + $comma = true; + } + if($params['address'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "address = '" . $rs['address'] . "', city = '" . $rs['city'] . "', zip = '" . $rs['zip'] . "', state = '" . $rs['state'] . "'"; + $comma = true; + } + if($params['website'] == "1") + { + if($comma) + { + $update .= ", "; + } + $update .= "web_site = '" . $rs['webSite'] . "'"; + $comma = true; + } + if(sizeof($params['emails']) == 1) + { + if($comma) + { + $update .= ", "; + } + $update .= "email1 = '" . $params['emails'][0]."'"; + $comma = true; + }else if(sizeof($params['emails']) == 2) + { + if($comma) + { + $update .= ", "; + $comma = false; + } + $update .= "email1 = '" . $params['emails'][0] . "', "; + $update .= "email2 = '" . $params['emails'][1] . "', "; + $comma = false; + } + if($comma){ + $update .= ", "; + } + $dateAvailable = $rs['dateAvailable']; + $dateParts = explode("-", $dateAvailable); + $dateAvailable = "20" . $dateParts[2] . "-" . $dateParts[0] . "-" . $dateParts[1] . " 00:00:00"; + $update .= "is_active = " . $rs['isActive'] . ", " . + "current_employer = '" . $rs['currentEmployer'] . "', " . + "current_pay = '" . $rs['currentPay'] . "', " . + "desired_pay = '" . $rs['desiredPay'] . "', " . + "can_relocate = " . $rs['canRelocate'] . ", " . + "best_time_to_call = '" . $rs['bestTimeToCall'] . "', " . + "is_hot = " . $rs['isHot'] . ", " . + "date_modified = NOW()"; + $comma = true; + if($rs['source'] != "" && $rs['source'] != "(none)") + { + if($comma){$update .= ", ";} + $update.= "source = IFNULL(CONCAT(source, ', ".$rs['source'] . "'), '" . $rs['source'] . "')"; + $comma = true; + } + if($rs['keySkills'] != "") + { + if($comma){$update .= ", ";} + $update .= "key_skills = IFNULL(CONCAT(key_skills, ', ".$rs['keySkills']."'), '" . $rs['keySkills'] . "')"; + $comma = true; + } + if($rs['notes'] != "") + { + if($comma){$update .= ", ";} + $update .= "notes = IFNULL(CONCAT(notes, ', ".$rs['notes']."'), '" . $rs['notes'] . "')"; + $comma = true; + } + if($rs['date_available'] != "") + { + if($comma){$update .= ", ";} + $update .= "date_available = '".$dateAvailable."' "; + } + + $sql = sprintf( + "UPDATE + candidate + SET + %s + WHERE + candidate_id = %s + AND + site_id = %s", + $update, + $this->_db->makeQueryInteger($oldCandidateID), + $this->_siteID + ); + + if($this->_db->query($sql)) + { + $sql = sprintf( + "DELETE FROM + candidate + WHERE + candidate_id = %s", + $this->_db->makeQueryInteger($newCandidateID) + ); + $this->_db->query($sql); + } + + //TO-DO: delete new candidate + } + + public function checkIfLinked($oldCandidateID, $newCandidateID) + { + if($oldCandidateID == $newCandidateID) + { + return true; + } + + $sql = sprintf( + "SELECT + candidate_duplicates.old_candidate_id as oldCandidateID, + candidate_duplicates.new_candidate_id as newCandidateID + FROM + candidate_duplicates + WHERE + candidate_duplicates.old_candidate_id = %s + AND + candidate_duplicates.new_candidate_id = %s + OR + candidate_duplicates.old_candidate_id = %s + AND + candidate_duplicates.new_candidate_id = %s", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_db->makeQueryInteger($newCandidateID), + $this->_db->makeQueryInteger($oldCandidateID) + ); + $rs = $this->_db->getAllAssoc($sql); + + if($rs && !$this->_db->isEOF()) + { + return true; + } + else + { + return false; + } + } } @@ -1312,7 +1674,7 @@ class CandidatesDataGrid extends DataGrid protected $_siteID; // FIXME: Fix ugly indenting - ~400 character lines = bad. - public function __construct($instanceName, $siteID, $parameters, $misc = 0) + public function __construct($instanceName, $siteID, $parameters, $misc = 0, $duplicates = 0) { $this->_db = DatabaseConnection::getInstance(); $this->_siteID = $siteID; @@ -1340,7 +1702,6 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) { $return .= \'\'; } - return $return; ', @@ -1351,7 +1712,9 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) ON candidate_joborder_submitted.candidate_id = candidate.candidate_id AND candidate_joborder_submitted.status >= '.PIPELINE_STATUS_SUBMITTED.' AND candidate_joborder_submitted.site_id = '.$this->_siteID.' - AND candidate_joborder_submitted.status != '.PIPELINE_STATUS_NOTINCONSIDERATION, + AND candidate_joborder_submitted.status != '.PIPELINE_STATUS_NOTINCONSIDERATION + + , 'pagerWidth' => 34, 'pagerOptional' => true, 'pagerNoTitle' => true, @@ -1621,6 +1984,16 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) WHERE t2.site_id = 1 AND t2.tag_id IN (". implode(",",$arguments)."))"; ') ); + + if($duplicates == 1) + { + $this->_classColumns['Attachments']['join'] .= ' INNER JOIN candidate_duplicates + ON candidate.candidate_id = + candidate_duplicates.new_candidate_id'; + $this->_classColumns['Attachments']['pagerWidth'] = 70; + $this->_classColumns['First Name']['pagerRender'] = 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'firstName\']).\'\';'; + $this->_classColumns['Last Name']['pagerRender'] = 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'lastName\']).\'\';'; + } if (US_ZIPS_ENABLED) { @@ -1650,7 +2023,7 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) } } - parent::__construct($instanceName, $parameters, $misc); + parent::__construct($instanceName, $parameters, $misc, $duplicates); } /** diff --git a/lib/DataGrid.php b/lib/DataGrid.php index 21977d197..b1a73a542 100755 --- a/lib/DataGrid.php +++ b/lib/DataGrid.php @@ -240,7 +240,7 @@ class DataGrid * @param integer dataGrid miscalaneous ID * @return object data grid */ - public static function get($indentifier, $parameters, $misc = 0) + public static function get($indentifier, $parameters, $misc = 0, $duplicates = 0) { /* This deals with loading a datagrid that was selected by use of the action / export box. */ if (isset($_REQUEST['dynamicArgument' . md5($indentifier)])) @@ -279,7 +279,7 @@ public static function get($indentifier, $parameters, $misc = 0) include_once (sprintf('modules/%s/dataGrids.php', $module)); - $dg = new $class($_SESSION['CATS']->getSiteID(), $parameters, $misc); + $dg = new $class($_SESSION['CATS']->getSiteID(), $parameters, $misc, $duplicates); return $dg; } diff --git a/lib/Duplicates.php b/lib/Duplicates.php deleted file mode 100644 index 0fdee4f89..000000000 --- a/lib/Duplicates.php +++ /dev/null @@ -1,1253 +0,0 @@ -_siteID = $siteID; - $this->_db = DatabaseConnection::getInstance(); - $this->extraFields = new ExtraFields($siteID, DATA_ITEM_CANDIDATE); - } - - - - - /** - * Removes a candidate and all associated records from the system. - * - * @param integer Candidate ID to delete. - * @return void - */ - public function delete($firstCandidateID, $secondCandidateID) - { - /* Delete the duplicate from candidate_duplicates. */ - $sql = sprintf( - "DELETE FROM - candidate_duplicates - WHERE - old_candidate_id = %s - AND - new_candidate_id = %s - OR - new_candidate_id = %s - AND - old_candidate_id = %s" - , - $this->_db->makeQueryInteger($firstCandidateID), - $this->_db->makeQueryInteger($secondCandidateID), - $this->_db->makeQueryInteger($firstCandidateID), - $this->_db->makeQueryInteger($secondCandidateID) - ); - $this->_db->query($sql); - } - - /** - * Returns all relevent candidate information for a given candidate ID. - * - * @param integer Candidate ID. - * @return array Associative result set array of candidate data, or array() - * if no records were returned. - */ - public function get($candidateID) - { - $sql = sprintf( - "SELECT - candidate.candidate_id AS candidateID, - candidate.is_active AS isActive, - candidate.first_name AS firstName, - candidate.middle_name AS middleName, - candidate.last_name AS lastName, - candidate.email1 AS email1, - candidate.email2 AS email2, - candidate.phone_home AS phoneHome, - candidate.phone_work AS phoneWork, - candidate.phone_cell AS phoneCell, - candidate.address AS address, - candidate.city AS city, - candidate.state AS state, - candidate.zip AS zip, - candidate.source AS source, - candidate.key_skills AS keySkills, - candidate.current_employer AS currentEmployer, - candidate.current_pay AS currentPay, - candidate.desired_pay AS desiredPay, - candidate.notes AS notes, - candidate.owner AS owner, - candidate.can_relocate AS canRelocate, - candidate.web_site AS webSite, - candidate.best_time_to_call AS bestTimeToCall, - candidate.is_hot AS isHot, - candidate.is_admin_hidden AS isAdminHidden, - DATE_FORMAT( - candidate.date_created, '%%m-%%d-%%y (%%h:%%i %%p)' - ) AS dateCreated, - DATE_FORMAT( - candidate.date_modified, '%%m-%%d-%%y (%%h:%%i %%p)' - ) AS dateModified, - COUNT( - candidate_joborder.joborder_id - ) AS pipeline, - ( - SELECT - COUNT(*) - FROM - candidate_joborder_status_history - WHERE - candidate_id = %s - AND - status_to = %s - AND - site_id = %s - ) AS submitted, - CONCAT( - candidate.first_name, ' ', candidate.last_name - ) AS candidateFullName, - CONCAT( - entered_by_user.first_name, ' ', entered_by_user.last_name - ) AS enteredByFullName, - CONCAT( - owner_user.first_name, ' ', owner_user.last_name - ) AS ownerFullName, - owner_user.email AS owner_email, - DATE_FORMAT( - candidate.date_available, '%%m-%%d-%%y' - ) AS dateAvailable, - eeo_ethnic_type.type AS eeoEthnicType, - eeo_veteran_type.type AS eeoVeteranType, - candidate.eeo_disability_status AS eeoDisabilityStatus, - candidate.eeo_gender AS eeoGender, - IF (candidate.eeo_gender = 'm', - 'Male', - IF (candidate.eeo_gender = 'f', - 'Female', - '')) - AS eeoGenderText - FROM - candidate - LEFT JOIN user AS entered_by_user - ON candidate.entered_by = entered_by_user.user_id - LEFT JOIN user AS owner_user - ON candidate.owner = owner_user.user_id - LEFT JOIN candidate_joborder - ON candidate.candidate_id = candidate_joborder.candidate_id - LEFT JOIN eeo_ethnic_type - ON eeo_ethnic_type.eeo_ethnic_type_id = candidate.eeo_ethnic_type_id - LEFT JOIN eeo_veteran_type - ON eeo_veteran_type.eeo_veteran_type_id = candidate.eeo_veteran_type_id - WHERE - candidate.candidate_id = %s - AND - candidate.site_id = %s - GROUP BY - candidate.candidate_id", - $this->_db->makeQueryInteger($candidateID), - PIPELINE_STATUS_SUBMITTED, - $this->_siteID, - $this->_db->makeQueryInteger($candidateID), - $this->_siteID - ); - $data = $this->_db->getAssoc($sql); - - $sql = sprintf( - "SELECT - candidate_duplicates.old_candidate_id AS duplicateTo - FROM - candidate_duplicates - WHERE - candidate_duplicates.new_candidate_id = %s", - $this->_db->makeQueryInteger($candidateID) - ); - $rs = $this->_db->getAllAssoc($sql); - $temp = array(); - if($rs && !$this->_db->isEOF()) - { - foreach($rs as $row) - { - array_push($temp, $row); - } - } - $data['isDuplicate'] = $temp; - return $data; - } - - - // FIXME: Document me. - public function getExport($IDs) - { - if (count($IDs) != 0) - { - $IDsValidated = array(); - - foreach ($IDs as $id) - { - $IDsValidated[] = $this->_db->makeQueryInteger($id); - } - - $criterion = 'AND candidate.candidate_id IN ('.implode(',', $IDsValidated).')'; - } - else - { - $criterion = ''; - } - - $sql = sprintf( - "SELECT - candidate.candidate_id AS candidateID, - candidate.last_name AS lastName, - candidate.first_name AS firstName, - candidate.phone_home AS phoneHome, - candidate.phone_cell AS phoneCell, - candidate.email1 AS email1, - candidate.key_skills as keySkills - FROM - candidate - WHERE - candidate.site_id = %s - %s - ORDER BY - candidate.last_name ASC, - candidate.first_name ASC", - $this->_siteID, - $criterion - ); - - return $this->_db->getAllAssoc($sql); - } - - /** - * Returns a candidate ID that matches the specified e-mail address. - * - * @param string Candidate e-mail address, - * @return integer Candidate ID, or -1 if no matching candidates were - * found. - */ - public function getIDByEmail($email) - { - $sql = sprintf( - "SELECT - candidate.candidate_id AS candidateID - FROM - candidate - WHERE - ( - candidate.email1 = %s - OR candidate.email2 = %s - ) - AND - candidate.site_id = %s", - $this->_db->makeQueryString($email), - $this->_db->makeQueryString($email), - $this->_siteID - ); - $rs = $this->_db->getAssoc($sql); - - if (empty($rs)) - { - return -1; - } - - 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 duplicates in the system. Useful - * for determining if the friendly "no candidates in system" - * should be displayed rather than the datagrid. - * - * @return integer Number of Duplicates in site. - */ - public function getCount() - { - $sql = sprintf( - "SELECT - COUNT(*) AS totalDuplicates - FROM - (SELECT * - FROM candidate_duplicates - WHERE - candidate_duplicates.site_id = %s - GROUP BY - candidate_duplicates.new_candidate_id) as innerQuery", - $this->_siteID - ); - return $this->_db->getColumn($sql, 0, 0); - } - - /** - * Returns the entire candidates list. - * - * @param boolean Include administratively hidden candidates? - * @return array Multi-dimensional associative result set array of - * candidates data, or array() if no records were returned. - */ - public function getAll($allowAdministrativeHidden = false) - { - if (!$allowAdministrativeHidden) - { - $adminHiddenCriterion = 'AND candidate.is_admin_hidden = 0'; - } - else - { - $adminHiddenCriterion = ''; - } - - $sql = sprintf( - "SELECT - candidate.candidate_id AS candidateID, - candidate.last_name AS lastName, - candidate.first_name AS firstName, - candidate.phone_home AS phoneHome, - candidate.phone_cell AS phoneCell, - candidate.email1 AS email1, - candidate.key_skills AS keySkills, - candidate.is_hot AS isHot, - DATE_FORMAT( - candidate.date_created, '%%m-%%d-%%y' - ) AS dateCreated, - DATE_FORMAT( - candidate.date_modified, '%%m-%%d-%%y' - ) AS dateModified, - candidate.date_created AS dateCreatedSort, - owner_user.first_name AS ownerFirstName, - owner_user.last_name AS ownerLastName - FROM - candidate - LEFT JOIN user AS owner_user - ON candidate.entered_by = user.user_id - WHERE - candidate.site_id = %s - %s - ORDER BY - candidate.last_name ASC, - candidate.first_name ASC", - $this->_siteID, - $adminHiddenCriterion - ); - - return $this->_db->getAllAssoc($sql); - } - - - - /** - * Updates a candidate's modified timestamp. - * - * @param integer Candidate ID. - * @return boolean Boolean was the query executed successfully? - */ - public function updateModified($candidateID) - { - $sql = sprintf( - "UPDATE - candidate - SET - date_modified = NOW() - WHERE - candidate_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($candidateID), - $this->_siteID - ); - - return (boolean) $this->_db->query($sql); - } - - public function removeDuplicity($oldCandidateID, $newCandidateID) - { - $sql = sprintf( - "DELETE FROM - candidate_duplicates - WHERE - candidate_duplicates.old_candidate_id = %s - AND - candidate_duplicates.new_candidate_id = %s", - $this->_db->makeQueryStringOrNULL($oldCandidateID), - $this->_db->makeQueryStringOrNULL($newCandidateID) - ); - $this->_db->query($sql); - } - - /** - * Adds a duplicate to the database. - * - * @param string first candidate ID. - * @param string second candidate ID. - * @return integer 1 on success, or -1 on failure. - */ - - public function addDuplicates($candidateID, $duplicates) - { - if(is_array($duplicates)) - { - foreach($duplicates as $duplicateID) - { - $sql = sprintf( - "INSERT INTO candidate_duplicates ( - old_candidate_id, - new_candidate_id, - site_id - ) - VALUES ( - %s, - %s, - %s - )", - $this->_db->makeQueryString($duplicateID), - $this->_db->makeQueryString($candidateID), - $this->_siteID - ); - $this->_db->query($sql); - } - } - else if($duplicates != "") - { - $sql = sprintf( - "INSERT INTO candidate_duplicates ( - old_candidate_id, - new_candidate_id, - site_id - ) - VALUES ( - %s, - %s, - %s - )", - $this->_db->makeQueryString($duplicates), - $this->_db->makeQueryString($candidateID), - $this->_siteID - ); - $this->_db->query($sql); - } - } - - - - public function mergeDuplicates($params, $rs) - { - $oldCandidateID = $params['oldCandidateID']; - $newCandidateID = $params['newCandidateID']; - $sql = sprintf( - "UPDATE - activity - SET - data_item_id = %s - WHERE - data_item_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - attachment - SET - data_item_id = %s - WHERE - data_item_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - calendar_event - SET - data_item_id = %s - WHERE - data_item_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "DELETE FROM - candidate_duplicates - WHERE - new_candidate_id = %s", - $this->_db->makeQueryInteger($newCandidateID) - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - candidate_duplicates - SET - old_candidate_id = %s - WHERE - old_candidate_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID) - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - candidate_joborder - SET - candidate_id = %s - WHERE - candidate_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - candidate_joborder_status_history - SET - candidate_id = %s - WHERE - candidate_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - candidate_tag - SET - candidate_id = %s - WHERE - candidate_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - $sql = sprintf( - "UPDATE - saved_list_entry - SET - data_item_id = %s - WHERE - data_item_id = %s - AND - site_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_siteID - ); - - echo $this->_db->query($sql); - - - $update = " "; - $comma = false; - - if($params['firstName'] == "1") - { - $update .= "first_name = '" . $rs['firstName']."'"; - $comma = true; - } - if($params['middleName'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "middle_name = '" . $rs['middleName']."'"; - $comma = true; - } - if($params['lastName'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "last_name = '" . $rs['lastName']."'"; - $comma = true; - } - if($params['phoneCell'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "phone_cell = '" . $rs['phoneCell']."'"; - $comma = true; - } - if($params['phoneWork'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "phone_work = '" . $rs['phoneWork']."'"; - $comma = true; - } - if($params['phoneHome'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "phone_home = '" . $rs['phoneHome']."'"; - $comma = true; - } - if($params['address'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "address = '" . $rs['address'] . "', city = '" . $rs['city'] . "', zip = '" . $rs['zip'] . "', state = '" . $rs['state'] . "'"; - $comma = true; - } - if($params['website'] == "1") - { - if($comma) - { - $update .= ", "; - } - $update .= "web_site = '" . $rs['webSite'] . "'"; - $comma = true; - } - if(sizeof($params['emails']) == 1) - { - if($comma) - { - $update .= ", "; - } - $update .= "email1 = '" . $params['emails'][0]."'"; - $comma = true; - }else if(sizeof($params['emails']) == 2) - { - if($comma) - { - $update .= ", "; - $comma = false; - } - if($params['emails'][0] != "") - { - $update .= "email1 = '" . $params['emails'][0] . "'"; - $comma = true; - } - if(comma) - { - $update .= ", "; - } - if($params['emails'][1] != "") - { - $update .= "email2 = '" . $params['emails'][1] . "'"; - } - $comma = true; - } - if(comma){ - $update .= ", "; - } - $dateAvailable = $rs['dateAvailable']; - $dateParts = explode("-", $dateAvailable); - $dateAvailable = "20" . $dateParts[2] . "-" . $dateParts[0] . "-" . $dateParts[1] . " 00:00:00"; - $update .= "is_active = " . $rs['isActive'] . ", " . - "current_employer = '" . $rs['currentEmployer'] . "', " . - "current_pay = '" . $rs['currentPay'] . "', " . - "desired_pay = '" . $rs['desiredPay'] . "', " . - "can_relocate = " . $rs['canRelocate'] . ", " . - "best_time_to_call = '" . $rs['bestTimeToCall'] . "', " . - "is_hot = " . $rs['isHot'] . ", " . - "date_modified = NOW()"; - $comma = true; - if($rs['source'] != "" && $rs['source'] != "(none)") - { - if($comma){$update .= ", ";} - $update.= "source = IFNULL(CONCAT(source, ', ".$rs['source'] . "'), '" . $rs['source'] . "')"; - $comma = true; - } - if($rs['keySkills'] != "") - { - if($comma){$update .= ", ";} - $update .= "key_skills = IFNULL(CONCAT(key_skills, ', ".$rs['keySkills']."'), '" . $rs['keySkills'] . "')"; - $comma = true; - } - if($rs['notes'] != "") - { - if($comma){$update .= ", ";} - $update .= "notes = IFNULL(CONCAT(notes, ', ".$rs['notes']."'), '" . $rs['notes'] . "')"; - $comma = true; - } - if($rs['date_available'] != "") - { - if($comma){$update .= ", ";} - $update .= "date_available = '".$dateAvailable."' "; - } - - $sql = sprintf( - "UPDATE - candidate - SET - %s - WHERE - candidate_id = %s - AND - site_id = %s", - $update, - $this->_db->makeQueryInteger($oldCandidateID), - $this->_siteID - ); - - if($this->_db->query($sql)) - { - $sql = sprintf( - "DELETE FROM - candidate - WHERE - candidate_id = %s", - $this->_db->makeQueryInteger($newCandidateID) - ); - echo $this->_db->query($sql); - } - - //TO-DO: delete new candidate - } - - public function checkIfLinked($oldCandidateID, $newCandidateID) - { - if($oldCandidateID == $newCandidateID) - { - return true; - } - - $sql = sprintf( - "SELECT - candidate_duplicates.old_candidate_id as oldCandidateID, - candidate_duplicates.new_candidate_id as newCandidateID - FROM - candidate_duplicates - WHERE - candidate_duplicates.old_candidate_id = %s - AND - candidate_duplicates.new_candidate_id = %s - OR - candidate_duplicates.old_candidate_id = %s - AND - candidate_duplicates.new_candidate_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_db->makeQueryInteger($newCandidateID), - $this->_db->makeQueryInteger($oldCandidateID) - ); - $rs = $this->_db->getAllAssoc($sql); - - if($rs && !$this->_db->isEOF()) - { - return true; - } - else - { - return false; - } - } -} - - -class DuplicatesDataGrid extends DataGrid -{ - protected $_siteID; - - // FIXME: Fix ugly indenting - ~400 character lines = bad. - public function __construct($instanceName, $siteID, $parameters, $misc = 0) - { - $this->_db = DatabaseConnection::getInstance(); - $this->_siteID = $siteID; - $this->_assignedCriterion = ""; - $this->_dataItemIDColumn = 'candidate.candidate_id'; - - $this->_classColumns = array( - 'Attachments' => array('select' => 'IF(candidate_joborder_submitted.candidate_joborder_id, 1, 0) AS submitted, - IF(attachment_id, 1, 0) AS attachmentPresent', - - 'pagerRender' => 'if ($rsData[\'submitted\'] == 1) - { - $return = \'\'; - } - else - { - $return = \'\'; - } - - if ($rsData[\'attachmentPresent\'] == 1) - { - $return .= \'\'; - } - else - { - $return .= \'\'; - } - - return $return; - ', - - 'join' => 'LEFT JOIN attachment - ON candidate.candidate_id = attachment.data_item_id - AND attachment.data_item_type = '.DATA_ITEM_CANDIDATE.' - LEFT JOIN candidate_joborder AS candidate_joborder_submitted - ON candidate_joborder_submitted.candidate_id = candidate.candidate_id - AND candidate_joborder_submitted.status >= '.PIPELINE_STATUS_SUBMITTED.' - AND candidate_joborder_submitted.site_id = '.$this->_siteID.' - AND candidate_joborder_submitted.status != '.PIPELINE_STATUS_NOTINCONSIDERATION.' - INNER JOIN candidate_duplicates - ON candidate.candidate_id = candidate_duplicates.new_candidate_id - ', - 'pagerWidth' => 70, - 'pagerOptional' => true, - 'pagerNoTitle' => true, - 'sizable' => false, - 'exportable' => false, - 'filterable' => false), - - 'First Name' => array('select' => 'candidate.first_name AS firstName', - 'pagerRender' => 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'firstName\']).\'\';', - 'sortableColumn' => 'firstName', - 'pagerWidth' => 75, - 'pagerOptional' => false, - 'alphaNavigation'=> true, - 'filter' => 'candidate.first_name'), - - 'Last Name' => array('select' => 'candidate.last_name AS lastName', - 'sortableColumn' => 'lastName', - 'pagerRender' => 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'lastName\']).\'\';', - 'pagerWidth' => 85, - 'pagerOptional' => false, - 'alphaNavigation' => true, - 'filter' => 'candidate.last_name'), - - 'E-Mail' => array('select' => 'candidate.email1 AS email1', - 'sortableColumn' => 'email1', - 'pagerWidth' => 80, - 'filter' => 'candidate.email1'), - - '2nd E-Mail' => array('select' => 'candidate.email2 AS email2', - 'sortableColumn' => 'email2', - 'pagerWidth' => 80, - 'filter' => 'candidate.email2'), - - 'Home Phone' => array('select' => 'candidate.phone_home AS phoneHome', - 'sortableColumn' => 'phoneHome', - 'pagerWidth' => 80, - 'filter' => 'candidate.phone_home'), - - 'Cell Phone' => array('select' => 'candidate.phone_cell AS phoneCell', - 'sortableColumn' => 'phoneCell', - 'pagerWidth' => 80, - 'filter' => 'candidate.phone_cell'), - - 'Work Phone' => array('select' => 'candidate.phone_work AS phoneWork', - 'sortableColumn' => 'phoneWork', - 'pagerWidth' => 80, - 'filter' => 'candidate.phone_work'), - - 'Address' => array('select' => 'candidate.address AS address', - 'sortableColumn' => 'address', - 'pagerWidth' => 250, - 'alphaNavigation' => true, - 'filter' => 'candidate.address'), - - 'City' => array('select' => 'candidate.city AS city', - 'sortableColumn' => 'city', - 'pagerWidth' => 80, - 'alphaNavigation' => true, - 'filter' => 'candidate.city'), - - - 'State' => array('select' => 'candidate.state AS state', - 'sortableColumn' => 'state', - 'filterType' => 'dropDown', - 'pagerWidth' => 50, - 'alphaNavigation' => true, - 'filter' => 'candidate.state'), - - 'Zip' => array('select' => 'candidate.zip AS zip', - 'sortableColumn' => 'zip', - 'pagerWidth' => 50, - 'filter' => 'candidate.zip'), - - 'Misc Notes' => array('select' => 'candidate.notes AS notes', - 'sortableColumn' => 'notes', - 'pagerWidth' => 300, - 'filter' => 'candidate.notes'), - - 'Web Site' => array('select' => 'candidate.web_site AS webSite', - 'pagerRender' => 'return \'\'.htmlspecialchars($rsData[\'webSite\']).\'\';', - 'sortableColumn' => 'webSite', - 'pagerWidth' => 80, - 'filter' => 'candidate.web_site'), - - 'Key Skills' => array('select' => 'candidate.key_skills AS keySkills', - 'pagerRender' => 'return substr(trim($rsData[\'keySkills\']), 0, 30) . (strlen(trim($rsData[\'keySkills\'])) > 30 ? \'...\' : \'\');', - 'sortableColumn' => 'keySkills', - 'pagerWidth' => 210, - 'filter' => 'candidate.key_skills'), - - 'Recent Status' => array('select' => '( - SELECT - CONCAT( - \'\', - candidate_joborder_status.short_description, - \'\' - ) - FROM - candidate_joborder - LEFT JOIN candidate_joborder_status - ON candidate_joborder_status.candidate_joborder_status_id = candidate_joborder.status - LEFT JOIN joborder - ON joborder.joborder_id = candidate_joborder.joborder_id - LEFT JOIN company - ON joborder.company_id = company.company_id - WHERE - candidate_joborder.candidate_id = candidate.candidate_id - ORDER BY - candidate_joborder.date_modified DESC - LIMIT 1 - ) AS lastStatus - ', - 'sort' => 'lastStatus', - 'pagerRender' => 'return $rsData[\'lastStatus\'];', - 'exportRender' => 'return $rsData[\'lastStatus\'];', - 'pagerWidth' => 140, - 'exportable' => false, - 'filterHaving' => 'lastStatus', - 'filterTypes' => '=~'), - - 'Recent Status (Extended)' => array('select' => '( - SELECT - CONCAT( - candidate_joborder_status.short_description, - \'
\', - \'\', - company.name, - \' - \', - \'\', - joborder.title, - \'\' - ) - FROM - candidate_joborder - LEFT JOIN candidate_joborder_status - ON candidate_joborder_status.candidate_joborder_status_id = candidate_joborder.status - LEFT JOIN joborder - ON joborder.joborder_id = candidate_joborder.joborder_id - LEFT JOIN company - ON joborder.company_id = company.company_id - WHERE - candidate_joborder.candidate_id = candidate.candidate_id - ORDER BY - candidate_joborder.date_modified DESC - LIMIT 1 - ) AS lastStatusLong - ', - 'sortableColumn' => 'lastStatusLong', - 'pagerRender' => 'return $rsData[\'lastStatusLong\'];', - 'pagerWidth' => 310, - 'exportable' => false, - 'filterable' => false), - - 'Source' => array('select' => 'candidate.source AS source', - 'sortableColumn' => 'source', - 'pagerWidth' => 140, - 'alphaNavigation' => true, - 'filter' => 'candidate.source'), - - 'Available' => array('select' => 'DATE_FORMAT(candidate.date_available, \'%m-%d-%y\') AS dateAvailable', - 'sortableColumn' => 'dateAvailable', - 'pagerWidth' => 60), - - 'Current Employer' => array('select' => 'candidate.current_employer AS currentEmployer', - 'sortableColumn' => 'currentEmployer', - 'pagerWidth' => 125, - 'alphaNavigation' => true, - 'filter' => 'candidate.current_employer'), - - 'Current Pay' => array('select' => 'candidate.current_pay AS currentPay', - 'sortableColumn' => 'currentPay', - 'pagerWidth' => 125, - 'filter' => 'candidate.current_pay', - 'filterTypes' => '===>=<'), - - 'Desired Pay' => array('select' => 'candidate.desired_pay AS desiredPay', - 'sortableColumn' => 'desiredPay', - 'pagerWidth' => 125, - 'filter' => 'candidate.desired_pay', - 'filterTypes' => '===>=<'), - - 'Can Relocate' => array('select' => 'candidate.can_relocate AS canRelocate', - 'pagerRender' => 'return ($rsData[\'canRelocate\'] == 0 ? \'No\' : \'Yes\');', - 'exportRender' => 'return ($rsData[\'canRelocate\'] == 0 ? \'No\' : \'Yes\');', - 'sortableColumn' => 'canRelocate', - 'pagerWidth' => 80, - 'filter' => 'candidate.can_relocate'), - - 'Owner' => array('select' => 'owner_user.first_name AS ownerFirstName,' . - 'owner_user.last_name AS ownerLastName,' . - 'CONCAT(owner_user.last_name, owner_user.first_name) AS ownerSort', - 'join' => 'LEFT JOIN user AS owner_user ON candidate.owner = owner_user.user_id', - 'pagerRender' => 'return StringUtility::makeInitialName($rsData[\'ownerFirstName\'], $rsData[\'ownerLastName\'], false, LAST_NAME_MAXLEN);', - 'exportRender' => 'return $rsData[\'ownerFirstName\'] . " " .$rsData[\'ownerLastName\'];', - 'sortableColumn' => 'ownerSort', - 'pagerWidth' => 75, - 'alphaNavigation' => true, - 'filter' => 'CONCAT(owner_user.first_name, owner_user.last_name)'), - - 'Created' => array('select' => 'DATE_FORMAT(candidate.date_created, \'%m-%d-%y\') AS dateCreated', - 'pagerRender' => 'return $rsData[\'dateCreated\'];', - 'sortableColumn' => 'dateCreatedSort', - 'pagerWidth' => 60, - 'filterHaving' => 'DATE_FORMAT(candidate.date_created, \'%m-%d-%y\')'), - - 'Modified' => array('select' => 'DATE_FORMAT(candidate.date_modified, \'%m-%d-%y\') AS dateModified', - 'pagerRender' => 'return $rsData[\'dateModified\'];', - 'sortableColumn' => 'dateModifiedSort', - 'pagerWidth' => 60, - 'pagerOptional' => false, - 'filterHaving' => 'DATE_FORMAT(candidate.date_modified, \'%m-%d-%y\')'), - - /* This one only works when called from the saved list view. Thats why it is not optional, filterable, or exportable. - * FIXME: Somehow make this defined in the associated savedListDataGrid class child. - */ - 'Added To List' => array('select' => 'DATE_FORMAT(saved_list_entry.date_created, \'%m-%d-%y\') AS dateAddedToList, - saved_list_entry.date_created AS dateAddedToListSort', - 'pagerRender' => 'return $rsData[\'dateAddedToList\'];', - 'sortableColumn' => 'dateAddedToListSort', - 'pagerWidth' => 60, - 'pagerOptional' => false, - 'filterable' => false, - 'exportable' => false), - - 'OwnerID' => array('select' => '', - 'filter' => 'candidate.owner', - 'pagerOptional' => false, - 'filterable' => false, - 'filterDescription' => 'Only My Candidates'), - - 'IsHot' => array('select' => '', - 'filter' => 'candidate.is_hot', - 'pagerOptional' => false, - 'filterable' => false, - 'filterDescription' => 'Only Hot Candidates'), - // Tags filtering - 'Tags' => array( - 'select' => '( - SELECT TRIM(GROUP_CONCAT(\' \',t2.title)) FROM candidate_tag t1 - LEFT JOIN tag t2 ON t1.tag_id = t2.tag_id - WHERE t1.candidate_id = candidate.candidate_id - GROUP BY candidate_id - ) as tags - ', - 'sortableColumn' => 'tags', - 'pagerRender' => 'return $rsData[\'tags\'];', - 'pagerOptional' => false, - 'pagerWidth' => 310, - 'exportable' => false, - 'filterable' => false, - - 'filterTypes' => '=#', - 'filterRender=#' => ' - return "candidate.candidate_id IN ( - SELECT t1.candidate_id tags FROM candidate t1 - LEFT JOIN candidate_tag t2 ON t1.candidate_id = t2.candidate_id - WHERE t2.site_id = 1 AND t2.tag_id IN (". implode(",",$arguments)."))"; - ') - ); - - if (US_ZIPS_ENABLED) - { - $this->_classColumns['Near Zipcode'] = - array('select' => 'candidate.zip AS zip', - 'filter' => 'candidate.zip', - 'pagerOptional' => false, - 'filterTypes' => '=@'); - } - - /* Extra fields get added as columns here. */ - $candidates = new Candidates($this->_siteID); - $extraFieldsRS = $candidates->extraFields->getSettings(); - foreach ($extraFieldsRS as $index => $data) - { - $fieldName = $data['fieldName']; - - if (!isset($this->_classColumns[$fieldName])) - { - $columnDefinition = $candidates->extraFields->getDataGridDefinition($index, $data, $this->_db); - - /* Return false for extra fields that should not be columns. */ - if ($columnDefinition !== false) - { - $this->_classColumns[$fieldName] = $columnDefinition; - } - } - } - - parent::__construct($instanceName, $parameters, $misc); - } - - /** - * Returns the sql statment for the pager. - * - * @return array Candidates data - */ - public function getSQL($selectSQL, $joinSQL, $whereSQL, $havingSQL, $orderSQL, $limitSQL, $distinct = '') - { - // FIXME: Factor out Session dependency. - if ($_SESSION['CATS']->isLoggedIn() && $_SESSION['CATS']->getAccessLevel() < ACCESS_LEVEL_MULTI_SA) - { - $adminHiddenCriterion = 'AND candidate.is_admin_hidden = 0'; - } - else - { - $adminHiddenCriterion = ''; - } - - if ($this->getMiscArgument() != 0) - { - $savedListID = (int) $this->getMiscArgument(); - $joinSQL .= ' INNER JOIN saved_list_entry - ON saved_list_entry.data_item_type = '.DATA_ITEM_CANDIDATE.' - AND saved_list_entry.data_item_id = candidate.candidate_id - AND saved_list_entry.site_id = '.$this->_siteID.' - AND saved_list_entry.saved_list_id = '.$savedListID; - } - else - { - $joinSQL .= ' LEFT JOIN saved_list_entry - ON saved_list_entry.data_item_type = '.DATA_ITEM_CANDIDATE.' - AND saved_list_entry.data_item_id = candidate.candidate_id - AND saved_list_entry.site_id = '.$this->_siteID; - } - - $sql = sprintf( - "SELECT SQL_CALC_FOUND_ROWS %s - candidate.candidate_id AS candidateID, - candidate.candidate_id AS exportID, - candidate.is_hot AS isHot, - candidate.date_modified AS dateModifiedSort, - candidate.date_created AS dateCreatedSort, - %s - FROM - candidate - %s - WHERE - candidate.site_id = %s - %s - %s - %s - GROUP BY candidate.candidate_id - %s - %s - %s", - $distinct, - $selectSQL, - $joinSQL, - $this->_siteID, - $adminHiddenCriterion, - (strlen($whereSQL) > 0) ? ' AND ' . $whereSQL : '', - $this->_assignedCriterion, - (strlen($havingSQL) > 0) ? ' HAVING ' . $havingSQL : '', - $orderSQL, - $limitSQL - ); - - return $sql; - } -} - -?> diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index 31353edeb..cce0cc79b 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -49,7 +49,6 @@ include_once('./lib/Questionnaire.php'); include_once('./lib/Tags.php'); include_once('./lib/Search.php'); -include_once('./lib/Duplicates.php'); class CandidatesUI extends UserInterface { @@ -74,7 +73,8 @@ public function __construct() $this->_moduleTabText = 'Candidates'; $this->_subTabs = array( 'Add Candidate' => CATSUtility::getIndexName() . '?m=candidates&a=add*al=' . ACCESS_LEVEL_EDIT, - 'Search Candidates' => CATSUtility::getIndexName() . '?m=candidates&a=search' + 'Search Candidates' => CATSUtility::getIndexName() . '?m=candidates&a=search', + 'View Duplicates' => CATSUtility::getIndexName() . '?m=candidates&a=viewDuplicates*al=' . ACCESS_LEVEL_SA ); } @@ -238,6 +238,37 @@ public function handleRequest() $this->onShowQuestionnaire(); break; + case 'viewDuplicates': + $this->showDuplicates(); + break; + + case 'showDuplicate': + $duplicate = 1; + $this->show($duplicate); + break; + + case 'linkDuplicate': + $this->findDuplicateCandidateSearch(); + break; + + /* Merge two duplicate candidates into the older one */ + case 'merge': + $this->mergeDuplicates(); + break; + + case 'mergeInfo': + $this->mergeDuplicatesInfo(); + break; + + /* Remove duplicity warning from a new candidate */ + case 'removeDuplicity': + $this->removeDuplicity(); + break; + + case 'addDuplicates': + $this->addDuplicates(); + break; + /* Main candidates page. */ case 'listByView': default: @@ -334,7 +365,7 @@ private function listByView($errMessage = '') /* * Called by handleRequest() to process loading the details page. */ - private function show() + private function show($duplicate = 0) { /* Is this a popup? */ if (isset($_GET['display']) && $_GET['display'] == 'popup') @@ -362,8 +393,15 @@ private function show() { $candidateID = $candidates->getIDByEmail($_GET['email']); } - - $data = $candidates->get($candidateID); + + if($duplicate == 0) + { + $data = $candidates->get($candidateID); + } + else + { + $data = $candidates->getWithDuplicity($candidateID); + } /* Bail out if we got an empty result set. */ if (empty($data)) @@ -613,8 +651,15 @@ private function show() $this->_template->assign('isDuplicate', 1); if (!eval(Hooks::get('CANDIDATE_SHOW'))) return; - - $this->_template->display('./modules/candidates/Show.tpl'); + if($duplicate == 0) + { + $this->_template->display('./modules/candidates/Show.tpl'); + } + else + { + $this->_template->display('./modules/candidates/ShowDuplicate.tpl'); + } + } /* @@ -2584,9 +2629,9 @@ private function _addCandidate($isModal, $directoryOverride = '') if (!eval(Hooks::get('CANDIDATE_ON_ADD_PRE'))) return; $candidates = new Candidates($this->_siteID); - + $duplicatesID = $candidates->checkDuplicity($firstName, $middleName, $lastName, $email1, $email2, $phoneHome, $phoneCell, $phoneWork, $address, $city); - + $candidateID = $candidates->add( $firstName, $middleName, @@ -2618,17 +2663,18 @@ private function _addCandidate($isModal, $directoryOverride = '') $disability ); + if ($candidateID <= 0) { return $candidateID; } - $duplicates = new Duplicates($this->_siteID); + $candidates = new Candidates($this->_siteID); if(sizeof($duplicatesID) > 0) { - $duplicates->addDuplicates($candidateID, $duplicatesID); + $candidates->addDuplicates($candidateID, $duplicatesID); } - + /* Update extra fields. */ $candidates->extraFields->setValuesOnEdit($candidateID); @@ -2807,7 +2853,6 @@ private function _addCandidate($isModal, $directoryOverride = '') // FIXME: Show parse errors! } - if (!eval(Hooks::get('CANDIDATE_ON_ADD_POST'))) return; return $candidateID; @@ -3317,6 +3362,191 @@ private function onShowQuestionnaire() $this->_template->display('./modules/candidates/Questionnaire.tpl'); } + + private function showDuplicates($errMessage = '') + { + if ($this->_accessLevel < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } + // Log message that shows up on the top of the list page + $topLog = ''; + + $dataGridProperties = DataGrid::getRecentParamaters("candidates:candidatesListByViewDataGrid"); + + /* If this is the first time we visited the datagrid this session, the recent paramaters will + * be empty. Fill in some default values. */ + if ($dataGridProperties == array()) + { + $dataGridProperties = array('rangeStart' => 0, + 'maxResults' => 15, + 'filterVisible' => false); + } + + $tags = new Tags($this->_siteID); + $tagsRS = $tags->getAll(); + + $dataGrid = DataGrid::get("candidates:candidatesListByViewDataGrid", $dataGridProperties, 0, 1); + + $candidates = new Candidates($this->_siteID); + $this->_template->assign('totalDuplicates', $candidates->getDuplicatesCount()); + + $this->_template->assign('active', $this); + $this->_template->assign('dataGrid', $dataGrid); + $this->_template->assign('userID', $_SESSION['CATS']->getUserID()); + $this->_template->assign('errMessage', $errMessage); + $this->_template->assign('topLog', $topLog); + $this->_template->assign('tagsRS', $tagsRS); + + if (!eval(Hooks::get('DUPLICATE_LIST_BY_VIEW'))) return; + + $this->_template->display('./modules/candidates/Duplicates.tpl'); + } + + private function findDuplicateCandidateSearch() + { + if ($this->_accessLevel < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } + $duplicateCandidateID = $_GET['candidateID']; + if($duplicateCandidateID == "") + { + $duplicateCandidateID = $_POST['candidateID']; + } + $query = $this->getTrimmedInput('wildCardString', $_POST); + $mode = $this->getTrimmedInput('mode', $_POST); + + /* Execute the search. */ + $search = new SearchCandidates($this->_siteID); + switch ($mode) + { + case 'searchByCandidateName': + $rs = $search->byFullName($query, 'candidate.last_name', 'ASC', true); + $resultsMode = true; + break; + + default: + $rs = $search->all($query, 'candidate.last_name', 'ASC', 'true'); + $resultsMode = false; + break; + } + + $candidates = new Candidates($this->_siteID); + + foreach ($rs as $rowIndex => $row) + { + $rs[$rowIndex]['duplicateCandidateID'] = $duplicateCandidateID; + if ($candidates->checkIfLinked($rs[$rowIndex]['candidateID'], $duplicateCandidateID)) + { + $rs[$rowIndex]['linked'] = true; + } + else + { + $rs[$rowIndex]['linked'] = false; + } + + if ($row['isHot'] == 1) + { + $rs[$rowIndex]['linkClass'] = 'jobLinkHot'; + } + else + { + $rs[$rowIndex]['linkClass'] = 'jobLinkCold'; + } + } + + if (!eval(Hooks::get('DUPLICATE_ON_LINK_DUPLICATES'))) return; + + $this->_template->assign('rs', $rs); + $this->_template->assign('isFinishedMode', false); + $this->_template->assign('isResultsMode', $resultsMode); + $this->_template->assign('duplicateCandidateID', $duplicateCandidateID); + $this->_template->display('./modules/candidates/LinkDuplicity.tpl'); + } + + private function mergeDuplicates() + { + if ($this->_accessLevel < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } + $candidates = new Candidates($this->_siteID); + $oldCandidateID = $_GET['oldCandidateID']; + $newCandidateID = $_GET['newCandidateID']; + + $rsOld = $candidates->getWithDuplicity($oldCandidateID); + $rsNew = $candidates->getWithDuplicity($newCandidateID); + + $this->_template->assign('isFinishedMode', false); + $this->_template->assign('rsOld', $rsOld); + $this->_template->assign('rsNew', $rsNew); + $this->_template->assign('oldCandidateID', $oldCandidateID); + $this->_template->assign('newCandidateID', $newCandidateID); + $this->_template->display('./modules/candidates/Merge.tpl'); + } + + private function mergeDuplicatesInfo() + { + if ($this->_accessLevel < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } + $candidates = new Candidates($this->_siteID); + $params = array(); + $params['firstName'] = $_POST['firstName']; + $params['middleName'] = $_POST['middleName']; + $params['lastName'] = $_POST['lastName']; + if(isset($_POST['email'])) + { + $params['emails'] = $_POST['email']; + } + else + { + $params['emails'] = array(); + } + $params['phoneCell'] = $_POST['phoneCell']; + $params['phoneWork'] = $_POST['phoneWork']; + $params['phoneHome'] = $_POST['phoneHome']; + $params['address'] = $_POST['address']; + $params['website'] = $_POST['website']; + $params['oldCandidateID'] = $_POST['oldCandidateID']; + $params['newCandidateID'] = $_POST['newCandidateID']; + + $candidates->mergeDuplicates($params, $candidates->getWithDuplicity($params['newCandidateID'])); + $this->_template->assign('isFinishedMode', true); + $this->_template->display('./modules/candidates/Merge.tpl'); + } + + private function removeDuplicity() + { + if ($this->_accessLevel < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } + $candidates = new Candidates($this->_siteID); + $oldCandidateID = $_GET['oldCandidateID']; + $newCandidateID = $_GET['newCandidateID']; + $candidates->removeDuplicity($oldCandidateID, $newCandidateID); + $url = CATSUtility::getIndexName()."?m=candidates&a=viewDuplicates"; + header("Location: " . $url); /* Redirect browser */ + exit(); + } + + + private function addDuplicates() + { + if ($this->_accessLevel < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } + $candidates = new Candidates($this->_siteID); + $oldCandidateID = $_GET['candidateID']; + $newCandidateID = $_GET['duplicateCandidateID']; + $candidates->addDuplicates($newCandidateID, $oldCandidateID); + $this->_template->assign('isFinishedMode', true); + $this->_template->display('./modules/candidates/LinkDuplicity.tpl'); + } } ?> diff --git a/modules/duplicates/Duplicates.tpl b/modules/candidates/Duplicates.tpl similarity index 96% rename from modules/duplicates/Duplicates.tpl rename to modules/candidates/Duplicates.tpl index 981fa5d44..946815773 100644 --- a/modules/duplicates/Duplicates.tpl +++ b/modules/candidates/Duplicates.tpl @@ -1,8 +1,8 @@ - + active); ?> dataGrid->getInstanceName());?> @@ -144,7 +144,7 @@



-
+
 


diff --git a/modules/duplicates/LinkDuplicity.tpl b/modules/candidates/LinkDuplicity.tpl similarity index 95% rename from modules/duplicates/LinkDuplicity.tpl rename to modules/candidates/LinkDuplicity.tpl index f1f571d36..39d031519 100644 --- a/modules/duplicates/LinkDuplicity.tpl +++ b/modules/candidates/LinkDuplicity.tpl @@ -1,8 +1,8 @@ - + isFinishedMode): ?>

Search for a candidate below, and then click on the candidate name to link this candidate as a duplicate to them.

- + duplicateCandidateID)?> /> @@ -63,7 +63,7 @@ diff --git a/src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php b/src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php new file mode 100644 index 000000000..6d9b222e9 --- /dev/null +++ b/src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php @@ -0,0 +1,28 @@ +mergeUrl = $mergeUrl; + $this->removeUrl = $removeUrl; + } + + protected function getMenuType() + { + return 'quickAction.DuplicateCandidateMenu'; + } + + protected function getParameters() + { + $parameters = parent::getParameters(); + $parameters[] = "'". $this->mergeUrl .";'"; + $parameters[] = "'". $this->removeUrl .";'"; + return $parameters; + } +} \ No newline at end of file From 035cbbdde0ab7f6c8cf6ef1ea6e7d3a2c4f31874 Mon Sep 17 00:00:00 2001 From: SvetoKrchnavy Date: Sat, 10 Dec 2016 18:01:30 +0100 Subject: [PATCH 06/92] quickaction for duplicates added --- modules/candidates/ShowDuplicate.tpl | 5 ++--- modules/candidates/quickAction-duplicates.js | 21 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 modules/candidates/quickAction-duplicates.js diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index 0aa70c273..517e84e19 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -9,16 +9,15 @@ * modify it under the terms of the GNU General Public License 2 * as published by the Free Software Foundation. */ -?> isPopup): ?> - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js')); ?> + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-duplicates.js')); ?> - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js')); ?> + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-duplicates.js')); ?> active); ?> diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js new file mode 100644 index 000000000..2f67ebe76 --- /dev/null +++ b/modules/candidates/quickAction-duplicates.js @@ -0,0 +1,21 @@ +quickAction.DuplicateCandidateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, mergeUrl, removeUrl) +{ + quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY); + this.mergeUrl = mergeUrl; + this.removeUrl = removeUrl; +} + +quickAction.DuplicateCandidateMenu.prototype = Object.create(quickAction.DefaultMenu.prototype); + +quickAction.DuplicateCandidateMenu.prototype.getOptions = function() +{ + return [ + new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), + new quickAction.LinkMenuOption('Remove duplicity warning', this.urlDecode(this.removeUrl), 1) + ]; +} + +quickAction.DuplicateCandidateMenu.prototype.urlDecode = function(url) +{ + return decodeURIComponent(url.replace(/\+/g, ' ')); +} From 4dff27a072c270ca228d2e487e4b3696c50f3bcc Mon Sep 17 00:00:00 2001 From: SvetoKrchnavy Date: Sat, 10 Dec 2016 18:35:55 +0100 Subject: [PATCH 07/92] rename of DuplicateCandidate to CandidateDuplicate --- modules/candidates/ShowDuplicate.tpl | 4 ++-- modules/candidates/quickAction-duplicates.js | 8 ++++---- ...tionMenu.php => CandidateDuplicateQuickActionMenu.php} | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/OpenCATS/UI/{DuplicateCandidateQuickActionMenu.php => CandidateDuplicateQuickActionMenu.php} (83%) diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index 517e84e19..955c6b6c2 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -12,7 +12,7 @@ isPopup): ?> data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-duplicates.js')); ?> @@ -36,7 +36,7 @@ use OpenCATS\UI\DuplicateCandidateQuickActionMenu; duplicate_warning data['isDuplicate'] as $item): ?> Duplicate' ?> - data['candidateID'], urlencode(CATSUtility::getIndexName().'?m=duplicates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js index 2f67ebe76..7814cb390 100644 --- a/modules/candidates/quickAction-duplicates.js +++ b/modules/candidates/quickAction-duplicates.js @@ -1,13 +1,13 @@ -quickAction.DuplicateCandidateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, mergeUrl, removeUrl) +quickAction.CandidateDuplicateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, mergeUrl, removeUrl) { quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY); this.mergeUrl = mergeUrl; this.removeUrl = removeUrl; } -quickAction.DuplicateCandidateMenu.prototype = Object.create(quickAction.DefaultMenu.prototype); +quickAction.CandidateDuplicateMenu.prototype = Object.create(quickAction.DefaultMenu.prototype); -quickAction.DuplicateCandidateMenu.prototype.getOptions = function() +quickAction.CandidateDuplicateMenu.prototype.getOptions = function() { return [ new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), @@ -15,7 +15,7 @@ quickAction.DuplicateCandidateMenu.prototype.getOptions = function() ]; } -quickAction.DuplicateCandidateMenu.prototype.urlDecode = function(url) +quickAction.CandidateDuplicateMenu.prototype.urlDecode = function(url) { return decodeURIComponent(url.replace(/\+/g, ' ')); } diff --git a/src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php similarity index 83% rename from src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php rename to src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php index 6d9b222e9..4cda64618 100644 --- a/src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php +++ b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php @@ -1,7 +1,7 @@ Date: Mon, 12 Dec 2016 10:51:40 +0100 Subject: [PATCH 08/92] tag fix --- modules/candidates/ShowDuplicate.tpl | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index 955c6b6c2..96cde5aae 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -10,7 +10,6 @@ * as published by the Free Software Foundation. */ - From 519d90b66a12b864812f03524efc07a854b70936 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 12 Dec 2016 11:13:38 +0100 Subject: [PATCH 09/92] quickaction fixes --- modules/candidates/ShowDuplicate.tpl | 4 ++-- src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index 96cde5aae..0b39f5564 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -38,8 +38,8 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; data['candidateID'], - urlencode(CATSUtility::getIndexName().'?m=duplicates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), - urlencode(CATSUtility::getIndexName().'?m=duplicates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] + urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), + urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] ))); ?> diff --git a/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php index 4cda64618..ac7f96716 100644 --- a/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php +++ b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php @@ -21,8 +21,8 @@ protected function getMenuType() protected function getParameters() { $parameters = parent::getParameters(); - $parameters[] = "'". $this->mergeUrl .";'"; - $parameters[] = "'". $this->removeUrl .";'"; + $parameters[] = "'". $this->mergeUrl ."'"; + $parameters[] = "'". $this->removeUrl ."'"; return $parameters; } } \ No newline at end of file From 5c7d3e3a03a9b8bfb7d29d1d75cee66585489409 Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Sat, 24 Dec 2016 00:50:55 +0100 Subject: [PATCH 10/92] acl related refactoring ifor duplicates --- config.php | 6 +++ modules/candidates/CandidatesUI.php | 54 +++++++++++--------- modules/candidates/Show.tpl | 2 +- modules/candidates/ShowDuplicate.tpl | 12 ++--- modules/candidates/quickAction-duplicates.js | 12 +++-- src/OpenCATS/UI/QuickActionMenu.php | 3 +- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/config.php b/config.php index b83496470..02d95e8a6 100755 --- a/config.php +++ b/config.php @@ -433,6 +433,12 @@ 'candidates.emailCandidates' 'candidates.show_questionnaire' 'candidates.list' + 'canidates.viewDuplicates' + 'canidates.linkDuplicate' + 'canidates.merge' + 'canidates.mergeInfo' + 'canidates.removeDuplicity' + 'canidates.addDuplicates' 'calendar.show' 'calendar.addEvent' 'calendar.editEvent' diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index d0645428f..513f16057 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -74,7 +74,7 @@ public function __construct() $this->_subTabs = array( 'Add Candidate' => CATSUtility::getIndexName() . '?m=candidates&a=add*al=' . ACCESS_LEVEL_EDIT . '@candidates.add', 'Search Candidates' => CATSUtility::getIndexName() . '?m=candidates&a=search', - 'View Duplicates' => CATSUtility::getIndexName() . '?m=candidates&a=viewDuplicates*al=' . ACCESS_LEVEL_SA + 'View Duplicates' => CATSUtility::getIndexName() . '?m=candidates&a=viewDuplicates*al=' . ACCESS_LEVEL_SA . '@canidates.viewDuplicates' ); } @@ -316,33 +316,61 @@ public function handleRequest() break; case 'viewDuplicates': + if ($this->getUserAccessLevel('canidates.viewDuplicates') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $this->showDuplicates(); break; case 'showDuplicate': + if ($this->getUserAccessLevel('canidates.viewDuplicates') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $duplicate = 1; $this->show($duplicate); break; case 'linkDuplicate': + if ($this->getUserAccessLevel('canidates.linkDuplicate') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $this->findDuplicateCandidateSearch(); break; /* Merge two duplicate candidates into the older one */ case 'merge': + if ($this->getUserAccessLevel('canidates.merge') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $this->mergeDuplicates(); break; case 'mergeInfo': + if ($this->getUserAccessLevel('canidates.mergeInfo') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $this->mergeDuplicatesInfo(); break; /* Remove duplicity warning from a new candidate */ case 'removeDuplicity': + if ($this->getUserAccessLevel('canidates.removeDuplicity') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $this->removeDuplicity(); break; case 'addDuplicates': + if ($this->getUserAccessLevel('canidates.addDuplicates') < ACCESS_LEVEL_SA) + { + CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); + } $this->addDuplicates(); break; @@ -3348,10 +3376,6 @@ private function onShowQuestionnaire() private function showDuplicates($errMessage = '') { - if ($this->_accessLevel < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } // Log message that shows up on the top of the list page $topLog = ''; @@ -3388,10 +3412,6 @@ private function showDuplicates($errMessage = '') private function findDuplicateCandidateSearch() { - if ($this->_accessLevel < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } $duplicateCandidateID = $_GET['candidateID']; if($duplicateCandidateID == "") { @@ -3450,10 +3470,6 @@ private function findDuplicateCandidateSearch() private function mergeDuplicates() { - if ($this->_accessLevel < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } $candidates = new Candidates($this->_siteID); $oldCandidateID = $_GET['oldCandidateID']; $newCandidateID = $_GET['newCandidateID']; @@ -3471,10 +3487,6 @@ private function mergeDuplicates() private function mergeDuplicatesInfo() { - if ($this->_accessLevel < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } $candidates = new Candidates($this->_siteID); $params = array(); $params['firstName'] = $_POST['firstName']; @@ -3503,10 +3515,6 @@ private function mergeDuplicatesInfo() private function removeDuplicity() { - if ($this->_accessLevel < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } $candidates = new Candidates($this->_siteID); $oldCandidateID = $_GET['oldCandidateID']; $newCandidateID = $_GET['newCandidateID']; @@ -3519,10 +3527,6 @@ private function removeDuplicity() private function addDuplicates() { - if ($this->_accessLevel < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } $candidates = new Candidates($this->_siteID); $oldCandidateID = $_GET['candidateID']; $newCandidateID = $_GET['duplicateCandidateID']; diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index 19c9d1e9a..2dd12cb46 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -435,7 +435,7 @@ use OpenCATS\UI\CandidateQuickActionMenu;      - accessLevel >= ACCESS_LEVEL_SA): ?> + getUserAccessLevel('candidates.linkDuplicate') >= ACCESS_LEVEL_SA): ?> add duplicate Link duplicate diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index 0b39f5564..d5cf7be36 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -231,7 +231,7 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; isPopup): ?> - +
- + _($data['firstName']); ?> @@ -72,7 +72,7 @@ - + _($data['lastName']); ?> @@ -116,7 +116,7 @@
- + _($data['firstName']); ?> @@ -125,7 +125,7 @@ - + _($data['lastName']); ?> diff --git a/modules/duplicates/Merge.tpl b/modules/candidates/Merge.tpl similarity index 94% rename from modules/duplicates/Merge.tpl rename to modules/candidates/Merge.tpl index 7454e0f4c..bebb5bd44 100644 --- a/modules/duplicates/Merge.tpl +++ b/modules/candidates/Merge.tpl @@ -1,8 +1,8 @@ - + isFinishedMode): ?> - + oldCandidateID; ?> /> newCandidateID; ?> /> @@ -59,23 +59,18 @@ - rsNew['email1'] != "" || $this->rsOld['email1'] != ""): ?> - - rsNew['email2'] != "" || $this->rsOld['email2'] != ""): ?> - - diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index cd37b660a..65deac013 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -433,7 +433,7 @@      accessLevel >= ACCESS_LEVEL_SA): ?> - + add duplicate Link duplicate      diff --git a/modules/duplicates/Show.tpl b/modules/candidates/ShowDuplicate.tpl similarity index 99% rename from modules/duplicates/Show.tpl rename to modules/candidates/ShowDuplicate.tpl index b226c50bb..97436fb75 100644 --- a/modules/duplicates/Show.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -1,8 +1,8 @@ data['isDuplicate'] as $item): ?> Duplicate' ?> data['candidateID'], - urlencode(CATSUtility::getIndexName().'?m=duplicates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), - urlencode(CATSUtility::getIndexName().'?m=duplicates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'])); ?> + urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), + urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'])); ?> diff --git a/modules/candidates/dataGrids.php b/modules/candidates/dataGrids.php index f6d34a11c..f71830c75 100755 --- a/modules/candidates/dataGrids.php +++ b/modules/candidates/dataGrids.php @@ -7,7 +7,7 @@ class candidatesListByViewDataGrid extends CandidatesDataGrid { - public function __construct($siteID, $parameters, $misc) + public function __construct($siteID, $parameters, $misc, $duplicates = 0) { /* Pager configuration. */ $this->_tableWidth = new Width(100, '%'); @@ -34,7 +34,7 @@ public function __construct($siteID, $parameters, $misc) ); parent::__construct("candidates:candidatesListByViewDataGrid", - $siteID, $parameters, $misc + $siteID, $parameters, $misc, $duplicates ); } diff --git a/modules/duplicates/DuplicatesUI.php b/modules/duplicates/DuplicatesUI.php deleted file mode 100644 index bd2429a4b..000000000 --- a/modules/duplicates/DuplicatesUI.php +++ /dev/null @@ -1,694 +0,0 @@ -_authenticationRequired = true; - $this->_moduleDirectory = 'duplicates'; - $this->_moduleName = 'duplicates'; - $this->_moduleTabText = 'Duplicates'; - } - - - public function handleRequest() - { - if (!eval(Hooks::get('DUPLICATES_HANDLE_REQUEST'))) return; - - $action = $this->getAction(); - switch ($action) - { - case 'show': - $this->show(); - break; - - case 'viewResume': - include_once('./lib/Search.php'); - - $this->viewResume(); - break; - - /* Hot List Page */ - case 'savedLists': - $this->savedList(); - break; - - case 'linkDuplicate': - $this->findDuplicateCandidateSearch(); - break; - - /* Merge two duplicate candidates into the older one */ - case 'merge': - $this->mergeDuplicates(); - break; - - case 'mergeInfo': - $this->mergeDuplicatesInfo(); - break; - - /* Remove duplicity warning from a new candidate */ - case 'removeDuplicity': - $this->removeDuplicity(); - break; - - case 'addDuplicates': - $this->addDuplicates(); - break; - - /* Main candidates page. */ - case 'listByView': - default: - $this->listByView(); - break; - } - } - - - /* - * Called by handleRequest() to process loading the list / main page. - */ - private function listByView($errMessage = '') - { - // Log message that shows up on the top of the list page - $topLog = ''; - - $dataGridProperties = DataGrid::getRecentParamaters("duplicates:duplicatesListByViewDataGrid"); - - /* If this is the first time we visited the datagrid this session, the recent paramaters will - * be empty. Fill in some default values. */ - if ($dataGridProperties == array()) - { - $dataGridProperties = array('rangeStart' => 0, - 'maxResults' => 15, - 'filterVisible' => false); - } - - //$newParameterArray = $this->_parameters; - $tags = new Tags($this->_siteID); - $tagsRS = $tags->getAll(); - //foreach($tagsRS as $r) $r['link'] = DataGrid::_makeControlLink($newParameterArray); - - $dataGrid = DataGrid::get("duplicates:duplicatesListByViewDataGrid", $dataGridProperties); - - $duplicates = new Duplicates($this->_siteID); - $this->_template->assign('totalDuplicates', $duplicates->getCount()); - - $this->_template->assign('active', $this); - $this->_template->assign('dataGrid', $dataGrid); - $this->_template->assign('userID', $_SESSION['CATS']->getUserID()); - $this->_template->assign('errMessage', $errMessage); - $this->_template->assign('topLog', $topLog); - $this->_template->assign('tagsRS', $tagsRS); - - if (!eval(Hooks::get('DUPLICATE_LIST_BY_VIEW'))) return; - - $this->_template->display('./modules/duplicates/Duplicates.tpl'); - } - - /* - * Called by handleRequest() to process loading the details page. - */ - private function show() - { - /* Is this a popup? */ - if (isset($_GET['display']) && $_GET['display'] == 'popup') - { - $isPopup = true; - } - else - { - $isPopup = false; - } - - /* Bail out if we don't have a valid candidate ID. */ - if (!$this->isRequiredIDValid('candidateID', $_GET) && !isset($_GET['email'])) - { - CommonErrors::fatal(COMMONERROR_BADINDEX, $this, 'Invalid candidate ID.'); - } - - $candidates = new Candidates($this->_siteID); - - if (isset($_GET['candidateID'])) - { - $candidateID = $_GET['candidateID']; - } - else - { - $candidateID = $candidates->getIDByEmail($_GET['email']); - } - - $data = $candidates->getWithDuplicity($candidateID); - - /* Bail out if we got an empty result set. */ - if (empty($data)) - { - CommonErrors::fatal(COMMONERROR_BADINDEX, $this, 'The specified candidate ID could not be found.'); - return; - } - - if ($data['isAdminHidden'] == 1 && $this->_accessLevel < ACCESS_LEVEL_MULTI_SA) - { - $this->listByView('This candidate is hidden - only a CATS Administrator can unlock the candidate.'); - return; - } - - /* We want to handle formatting the city and state here instead - * of in the template. - */ - $data['cityAndState'] = StringUtility::makeCityStateString( - $data['city'], $data['state'] - ); - - /* - * Replace newlines with
, fix HTML "special" characters, and - * strip leading empty lines and spaces. - */ - $data['notes'] = trim( - nl2br(htmlspecialchars($data['notes'], ENT_QUOTES)) - ); - - /* Chop $data['notes'] to make $data['shortNotes']. */ - if (strlen($data['notes']) > self::NOTES_MAXLEN) - { - $data['shortNotes'] = substr( - $data['notes'], 0, self::NOTES_MAXLEN - ); - $isShortNotes = true; - } - else - { - $data['shortNotes'] = $data['notes']; - $isShortNotes = false; - } - - /* Format "can relocate" status. */ - if ($data['canRelocate'] == 1) - { - $data['canRelocate'] = 'Yes'; - } - else - { - $data['canRelocate'] = 'No'; - } - - if ($data['isHot'] == 1) - { - $data['titleClass'] = 'jobTitleHot'; - } - else - { - $data['titleClass'] = 'jobTitleCold'; - } - - $attachments = new Attachments($this->_siteID); - $attachmentsRS = $attachments->getAll( - DATA_ITEM_CANDIDATE, $candidateID - ); - - foreach ($attachmentsRS as $rowNumber => $attachmentsData) - { - /* If profile image is not local, force it to be local. */ - if ($attachmentsData['isProfileImage'] == 1) - { - $attachments->forceAttachmentLocal($attachmentsData['attachmentID']); - } - - /* Show an attachment icon based on the document's file type. */ - $attachmentIcon = strtolower( - FileUtility::getAttachmentIcon( - $attachmentsRS[$rowNumber]['originalFilename'] - ) - ); - - $attachmentsRS[$rowNumber]['attachmentIcon'] = $attachmentIcon; - - /* If the text field has any text, show a preview icon. */ - if ($attachmentsRS[$rowNumber]['hasText']) - { - $attachmentsRS[$rowNumber]['previewLink'] = sprintf( - '(Preview)', - CATSUtility::getIndexName(), - $attachmentsRS[$rowNumber]['attachmentID'] - ); - } - else - { - $attachmentsRS[$rowNumber]['previewLink'] = ' '; - } - } - $pipelines = new Pipelines($this->_siteID); - $pipelinesRS = $pipelines->getCandidatePipeline($candidateID); - - $sessionCookie = $_SESSION['CATS']->getCookie(); - - /* Format pipeline data. */ - foreach ($pipelinesRS as $rowIndex => $row) - { - /* Hot jobs [can] have different title styles than normal - * jobs. - */ - if ($row['isHot'] == 1) - { - $pipelinesRS[$rowIndex]['linkClass'] = 'jobLinkHot'; - } - else - { - $pipelinesRS[$rowIndex]['linkClass'] = 'jobLinkCold'; - } - - $pipelinesRS[$rowIndex]['ownerAbbrName'] = StringUtility::makeInitialName( - $pipelinesRS[$rowIndex]['ownerFirstName'], - $pipelinesRS[$rowIndex]['ownerLastName'], - false, - LAST_NAME_MAXLEN - ); - - $pipelinesRS[$rowIndex]['addedByAbbrName'] = StringUtility::makeInitialName( - $pipelinesRS[$rowIndex]['addedByFirstName'], - $pipelinesRS[$rowIndex]['addedByLastName'], - false, - LAST_NAME_MAXLEN - ); - - $pipelinesRS[$rowIndex]['ratingLine'] = TemplateUtility::getRatingObject( - $pipelinesRS[$rowIndex]['ratingValue'], - $pipelinesRS[$rowIndex]['candidateJobOrderID'], - $sessionCookie - ); - } - - $activityEntries = new ActivityEntries($this->_siteID); - $activityRS = $activityEntries->getAllByDataItem($candidateID, DATA_ITEM_CANDIDATE); - if (!empty($activityRS)) - { - foreach ($activityRS as $rowIndex => $row) - { - if (empty($activityRS[$rowIndex]['notes'])) - { - $activityRS[$rowIndex]['notes'] = '(No Notes)'; - } - - if (empty($activityRS[$rowIndex]['jobOrderID']) || - empty($activityRS[$rowIndex]['regarding'])) - { - $activityRS[$rowIndex]['regarding'] = 'General'; - } - - $activityRS[$rowIndex]['enteredByAbbrName'] = StringUtility::makeInitialName( - $activityRS[$rowIndex]['enteredByFirstName'], - $activityRS[$rowIndex]['enteredByLastName'], - false, - LAST_NAME_MAXLEN - ); - } - } - - /* Get upcoming calendar entries. */ - $calendarRS = $candidates->getUpcomingEvents($candidateID); - if (!empty($calendarRS)) - { - foreach ($calendarRS as $rowIndex => $row) - { - $calendarRS[$rowIndex]['enteredByAbbrName'] = StringUtility::makeInitialName( - $calendarRS[$rowIndex]['enteredByFirstName'], - $calendarRS[$rowIndex]['enteredByLastName'], - false, - LAST_NAME_MAXLEN - ); - } - } - - /* Get extra fields. */ - $extraFieldRS = $candidates->extraFields->getValuesForShow($candidateID); - - /* Add an MRU entry. */ - $_SESSION['CATS']->getMRU()->addEntry( - DATA_ITEM_CANDIDATE, $candidateID, $data['firstName'] . ' ' . $data['lastName'] - ); - - /* Is the user an admin - can user see history? */ - if ($this->_accessLevel < ACCESS_LEVEL_DEMO) - { - $privledgedUser = false; - } - else - { - $privledgedUser = true; - } - - $EEOSettings = new EEOSettings($this->_siteID); - $EEOSettingsRS = $EEOSettings->getAll(); - $EEOValues = array(); - - /* Make a list of all EEO related values so they can be positioned by index - * rather than static positioning (like extra fields). */ - if ($EEOSettingsRS['enabled'] == 1) - { - if ($EEOSettingsRS['genderTracking'] == 1) - { - $EEOValues[] = array('fieldName' => 'Gender', 'fieldValue' => $data['eeoGenderText']); - } - if ($EEOSettingsRS['ethnicTracking'] == 1) - { - $EEOValues[] = array('fieldName' => 'Ethnicity', 'fieldValue' => $data['eeoEthnicType']); - } - if ($EEOSettingsRS['veteranTracking'] == 1) - { - $EEOValues[] = array('fieldName' => 'Veteran Status', 'fieldValue' => $data['eeoVeteranType']); - } - if ($EEOSettingsRS['disabilityTracking'] == 1) - { - $EEOValues[] = array('fieldName' => 'Disability Status', 'fieldValue' => $data['eeoDisabilityStatus']); - } - } - - $tags = new Tags($this->_siteID); - - $questionnaire = new Questionnaire($this->_siteID); - $questionnaires = $questionnaire->getCandidateQuestionnaires($candidateID); - - $this->_template->assign('active', $this); - $this->_template->assign('questionnaires', $questionnaires); - $this->_template->assign('data', $data); - $this->_template->assign('isShortNotes', $isShortNotes); - $this->_template->assign('attachmentsRS', $attachmentsRS); - $this->_template->assign('pipelinesRS', $pipelinesRS); - $this->_template->assign('activityRS', $activityRS); - $this->_template->assign('calendarRS', $calendarRS); - $this->_template->assign('extraFieldRS', $extraFieldRS); - $this->_template->assign('candidateID', $candidateID); - $this->_template->assign('isPopup', $isPopup); - $this->_template->assign('EEOSettingsRS', $EEOSettingsRS); - $this->_template->assign('EEOValues', $EEOValues); - $this->_template->assign('privledgedUser', $privledgedUser); - $this->_template->assign('sessionCookie', $_SESSION['CATS']->getCookie()); - $this->_template->assign('tagsRS', $tags->getAll()); - $this->_template->assign('assignedTags', $tags->getCandidateTagsTitle($candidateID)); - - if (!eval(Hooks::get('DUPLICATES_SHOW'))) return; - - $this->_template->display('./modules/duplicates/Show.tpl'); - } - - - - - /* - * Called by handleRequest() to process showing a resume preview. - */ - private function viewResume() - { - /* Bail out if we don't have a valid candidate ID. */ - if (!$this->isRequiredIDValid('attachmentID', $_GET)) - { - CommonErrors::fatal(COMMONERROR_BADINDEX, $this, 'Invalid attachment ID.'); - } - - $attachmentID = $_GET['attachmentID']; - - /* Get the search string. */ - $query = $this->getTrimmedInput('wildCardString', $_GET); - - /* Get resume text. */ - $candidates = new Candidates($this->_siteID); - $data = $candidates->getResume($attachmentID); - - if (!empty($data)) - { - /* Keyword highlighting. */ - $data['text'] = SearchUtility::makePreview($query, $data['text']); - } - - if (!eval(Hooks::get('CANDIDATE_VIEW_RESUME'))) return; - - $this->_template->assign('active', $this); - $this->_template->assign('data', $data); - $this->_template->display('./modules/candidates/ResumeView.tpl'); - } - - - - /** - * Formats SQL result set for display. This is factored out for code - * clarity. - * - * @param array result set from listByView() - * @return array formatted result set - */ - private function _formatListByViewResults($resultSet) - { - if (empty($resultSet)) - { - return $resultSet; - } - - foreach ($resultSet as $rowIndex => $row) - { - if ($resultSet[$rowIndex]['isHot'] == 1) - { - $resultSet[$rowIndex]['linkClass'] = 'jobLinkHot'; - } - else - { - $resultSet[$rowIndex]['linkClass'] = 'jobLinkCold'; - } - - if (!empty($resultSet[$rowIndex]['ownerFirstName'])) - { - $resultSet[$rowIndex]['ownerAbbrName'] = StringUtility::makeInitialName( - $resultSet[$rowIndex]['ownerFirstName'], - $resultSet[$rowIndex]['ownerLastName'], - false, - LAST_NAME_MAXLEN - ); - } - else - { - $resultSet[$rowIndex]['ownerAbbrName'] = 'None'; - } - - if ($resultSet[$rowIndex]['submitted'] == 1) - { - $resultSet[$rowIndex]['iconTag'] = ''; - } - else - { - $resultSet[$rowIndex]['iconTag'] = ''; - } - - if ($resultSet[$rowIndex]['attachmentPresent'] == 1) - { - $resultSet[$rowIndex]['iconTag'] .= ''; - } - else - { - $resultSet[$rowIndex]['iconTag'] .= ''; - } - - - if (empty($resultSet[$rowIndex]['keySkills'])) - { - $resultSet[$rowIndex]['keySkills'] = ' '; - } - else - { - $resultSet[$rowIndex]['keySkills'] = htmlspecialchars( - $resultSet[$rowIndex]['keySkills'] - ); - } - - /* Truncate Key Skills to fit the column width */ - if (strlen($resultSet[$rowIndex]['keySkills']) > self::TRUNCATE_KEYSKILLS) - { - $resultSet[$rowIndex]['keySkills'] = substr( - $resultSet[$rowIndex]['keySkills'], - 0, - self::TRUNCATE_KEYSKILLS - ) . "..."; - } - } - - return $resultSet; - } - - /* - * Called by handleRequest() to handle processing an "Add to a Job Order - * Pipeline" search and displaying the results in the modal dialog, or - * to show the initial dialog. - */ - private function findDuplicateCandidateSearch() - { - $duplicateCandidateID = $_GET['candidateID']; - if($duplicateCandidateID == "") - { - $duplicateCandidateID = $_POST['candidateID']; - } - $query = $this->getTrimmedInput('wildCardString', $_POST); - $mode = $this->getTrimmedInput('mode', $_POST); - - /* Execute the search. */ - $search = new SearchCandidates($this->_siteID); - switch ($mode) - { - case 'searchByCandidateName': - $rs = $search->byFullName($query, 'candidate.last_name', 'ASC', true); - $resultsMode = true; - break; - - default: - $rs = $search->all($query, 'candidate.last_name', 'ASC', 'true'); - $resultsMode = false; - break; - } - - $duplicates = new Duplicates($this->_siteID); - - foreach ($rs as $rowIndex => $row) - { - $rs[$rowIndex]['duplicateCandidateID'] = $duplicateCandidateID; - if ($duplicates->checkIfLinked($rs[$rowIndex]['candidateID'], $duplicateCandidateID)) - { - $rs[$rowIndex]['linked'] = true; - } - else - { - $rs[$rowIndex]['linked'] = false; - } - - if ($row['isHot'] == 1) - { - $rs[$rowIndex]['linkClass'] = 'jobLinkHot'; - } - else - { - $rs[$rowIndex]['linkClass'] = 'jobLinkCold'; - } - } - - if (!eval(Hooks::get('DUPLICATE_ON_LINK_DUPLICATES'))) return; - - $this->_template->assign('rs', $rs); - $this->_template->assign('isFinishedMode', false); - $this->_template->assign('isResultsMode', $resultsMode); - $this->_template->assign('duplicateCandidateID', $duplicateCandidateID); - $this->_template->display('./modules/duplicates/LinkDuplicity.tpl'); - } - - private function mergeDuplicates() - { - $duplicates = new Duplicates($this->_siteID); - $candidates = new Candidates($this->_siteID); - $oldCandidateID = $_GET['oldCandidateID']; - $newCandidateID = $_GET['newCandidateID']; - - $rsOld = $candidates->getWithDuplicity($oldCandidateID); - $rsNew = $candidates->getWithDuplicity($newCandidateID); - - $this->_template->assign('isFinishedMode', false); - $this->_template->assign('rsOld', $rsOld); - $this->_template->assign('rsNew', $rsNew); - $this->_template->assign('oldCandidateID', $oldCandidateID); - $this->_template->assign('newCandidateID', $newCandidateID); - $this->_template->display('./modules/duplicates/Merge.tpl'); - } - - private function mergeDuplicatesInfo() - { - $duplicates = new Duplicates($this->_siteID); - $candidates = new Candidates($this->_siteID); - $params = array(); - $params['firstName'] = $_POST['firstName']; - $params['middleName'] = $_POST['middleName']; - $params['lastName'] = $_POST['lastName']; - if(isset($_POST['email'])) - { - $params['emails'] = $_POST['email']; - } - else - { - $params['emails'] = array(); - } - $params['phoneCell'] = $_POST['phoneCell']; - $params['phoneWork'] = $_POST['phoneWork']; - $params['phoneHome'] = $_POST['phoneHome']; - $params['address'] = $_POST['address']; - $params['website'] = $_POST['website']; - $params['oldCandidateID'] = $_POST['oldCandidateID']; - $params['newCandidateID'] = $_POST['newCandidateID']; - - $duplicates->mergeDuplicates($params, $candidates->getWithDuplicity($params['newCandidateID'])); - $this->_template->assign('isFinishedMode', true); - $this->_template->display('./modules/duplicates/Merge.tpl'); - } - - private function removeDuplicity() - { - $duplicates = new Duplicates($this->_siteID); - $oldCandidateID = $_GET['oldCandidateID']; - $newCandidateID = $_GET['newCandidateID']; - $duplicates->removeDuplicity($oldCandidateID, $newCandidateID); - $url = CATSUtility::getIndexName()."?m=duplicates"; - header("Location: " . $url); /* Redirect browser */ - exit(); - } - - - private function addDuplicates() - { - $duplicates = new Duplicates($this->_siteID); - $oldCandidateID = $_GET['candidateID']; - $newCandidateID = $_GET['duplicateCandidateID']; - $duplicates->addDuplicates($newCandidateID, $oldCandidateID); - $this->_template->assign('isFinishedMode', true); - $this->_template->display('./modules/duplicates/LinkDuplicity.tpl'); - } -} - -?> diff --git a/modules/duplicates/Error.tpl b/modules/duplicates/Error.tpl deleted file mode 100644 index 5da7f6eeb..000000000 --- a/modules/duplicates/Error.tpl +++ /dev/null @@ -1,37 +0,0 @@ - - - -active); ?> -
- - -
-
E-mails (max. 2) 
rsOld['email1'] == '') ? '(none)' : ($this->rsOld['email1']); ?> rsNew['email1'] == '') ? '(none)' : ($this->rsNew['email1']); ?>
rsOld['email2'] == '') ? '(none)' : ($this->rsOld['email2']); ?> rsNew['email2'] == '') ? '(none)' : ($this->rsNew['email2']); ?>
Cell phone 
- - - - -
- Contacts  -

Duplicates: Error

- -

- A fatal error has occurred.
-
- errorMessage); ?> -

- - -
- diff --git a/modules/duplicates/dataGrids.php b/modules/duplicates/dataGrids.php deleted file mode 100644 index a69fa143e..000000000 --- a/modules/duplicates/dataGrids.php +++ /dev/null @@ -1,141 +0,0 @@ -_tableWidth = new Width(100, '%'); - $this->_defaultAlphabeticalSortBy = 'lastName'; - $this->ajaxMode = false; - $this->showExportCheckboxes = true; //BOXES WILL NOT APPEAR UNLESS SQL ROW exportID IS RETURNED! - $this->showActionArea = true; - $this->showChooseColumnsBox = true; - $this->allowResizing = true; - - $this->defaultSortBy = 'dateModifiedSort'; - $this->defaultSortDirection = 'DESC'; - - $this->_defaultColumns = array( - array('name' => 'Attachments', 'width' => 31), - array('name' => 'First Name', 'width' => 75), - array('name' => 'Last Name', 'width' => 85), - array('name' => 'City', 'width' => 75), - array('name' => 'State', 'width' => 50), - array('name' => 'Key Skills', 'width' => 215), - array('name' => 'Owner', 'width' => 65), - array('name' => 'Created', 'width' => 60), - array('name' => 'Modified', 'width' => 60), - ); - - parent::__construct("duplicates:duplicatesListByViewDataGrid", - $siteID, $parameters, $misc - ); - } - - - /** - * Adds more options to the action area on the pager. Overloads - * DataGrid Inner Action Area function. - * - * @return html innerActionArea commands. - */ - public function getInnerActionArea() - { - //TODO: Add items: - // - Add to List - // - Add to Pipeline - // - Mass set rank (depends on each candidate having their own personal rank - are we going to do this?) - $html = ''; - - $html .= $this->getInnerActionAreaItemPopup('Add To List', CATSUtility::getIndexName().'?m=lists&a=addToListFromDatagridModal&dataItemType='.DATA_ITEM_CANDIDATE, 450, 350); - $html .= $this->getInnerActionAreaItemPopup('Add To Pipeline', CATSUtility::getIndexName().'?m=candidates&a=considerForJobSearch', 750, 460); - if(MAIL_MAILER != 0) - { - $html .= $this->getInnerActionAreaItem('Send E-Mail', CATSUtility::getIndexName().'?m=candidates&a=emailCandidates'); - } - $html .= $this->getInnerActionAreaItem('Export', CATSUtility::getIndexName().'?m=export&a=exportByDataGrid'); - - $html .= parent::getInnerActionArea(); - - return $html; - } -} - -class duplicatesSavedListByViewDataGrid extends DuplicatesDataGrid -{ - public function __construct($siteID, $parameters, $misc) - { - $this->_tableWidth = new Width(100, '%');; - $this->_defaultAlphabeticalSortBy = 'lastName'; - $this->ajaxMode = false; - $this->showExportCheckboxes = true; //BOXES WILL NOT APPEAR UNLESS SQL ROW exportID IS RETURNED! - $this->showActionArea = true; - $this->showChooseColumnsBox = true; - $this->allowResizing = true; - - $this->defaultSortBy = 'dateModifiedSort'; - $this->defaultSortDirection = 'DESC'; - - $this->_defaultColumns = array( - array('name' => 'Attachments', 'width' => 31), - array('name' => 'First Name', 'width' => 75), - array('name' => 'Last Name', 'width' => 85), - array('name' => 'City', 'width' => 75), - array('name' => 'State', 'width' => 50), - array('name' => 'Key Skills', 'width' => 200), - array('name' => 'Owner', 'width' => 65), - array('name' => 'Modified', 'width' => 60), - array('name' => 'Added To List', 'width' => 75), - ); - - parent::__construct("duplicates:duplicatesSavedListByViewDataGrid", - $siteID, $parameters, $misc - ); - } - - /** - * Adds more options to the action area on the pager. Overloads - * DataGrid Inner Action Area function. - * - * @return html innerActionArea commands. - */ - public function getInnerActionArea() - { - //TODO: Add items: - // - Add to List - // - Add to Pipeline - // - Mass set rank (depends on each candidate having their own personal rank - are we going to do this?) - $html = ''; - - $html .= $this->getInnerActionAreaItem('Remove From This List', CATSUtility::getIndexName().'?m=lists&a=removeFromListDatagrid&dataItemType='.DATA_ITEM_CANDIDATE.'&savedListID='.$this->getMiscArgument(), false); - $html .= $this->getInnerActionAreaItemPopup('Add To Pipeline', CATSUtility::getIndexName().'?m=candidates&a=considerForJobSearch', 750, 460); - if(MAIL_MAILER != 0) - { - $html .= $this->getInnerActionAreaItem('Send E-Mail', CATSUtility::getIndexName().'?m=candidates&a=emailCandidates'); - } - $html .= $this->getInnerActionAreaItem('Export', CATSUtility::getIndexName().'?m=export&a=exportByDataGrid'); - - $html .= parent::getInnerActionArea(); - - return $html; - } -} - - -?> \ No newline at end of file From cc38c5dbafa0553e271c6e3ec75258209783f6a0 Mon Sep 17 00:00:00 2001 From: SvetoKrchnavy Date: Sat, 10 Dec 2016 17:15:22 +0100 Subject: [PATCH 05/92] added duplicates quick action menu --- modules/candidates/ShowDuplicate.tpl | 14 ++++++++-- .../UI/DuplicateCandidateQuickActionMenu.php | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 src/OpenCATS/UI/DuplicateCandidateQuickActionMenu.php diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index 97436fb75..0aa70c273 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -10,6 +10,11 @@ * as published by the Free Software Foundation. */ ?> + + isPopup): ?> data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js')); ?> @@ -32,9 +37,12 @@ duplicate_warning data['isDuplicate'] as $item): ?> Duplicate' ?> - data['candidateID'], - urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), - urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'])); ?> + data['candidateID'], + urlencode(CATSUtility::getIndexName().'?m=duplicates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), + urlencode(CATSUtility::getIndexName().'?m=duplicates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] + ))); ?>
isPopup): ?> - accessLevel >= ACCESS_LEVEL_DELETE): ?> + getUserAccessLevel('candidates.deleteAttachment') >= ACCESS_LEVEL_DELETE): ?> @@ -458,7 +458,7 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; isPopup): ?> - getAccessLevel() >= ACCESS_LEVEL_EDIT && !$_SESSION['CATS']->hasUserCategory('sourcer')): ?> + getUserAccessLevel('pipelines.screening') >= ACCESS_LEVEL_EDIT && !$_SESSION['CATS']->hasUserCategory('sourcer')): ?> @@ -467,12 +467,12 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; - accessLevel >= ACCESS_LEVEL_EDIT): ?> + getUserAccessLevel('pipelines.addActivityChangeStatus') >= ACCESS_LEVEL_EDIT): ?> - accessLevel >= ACCESS_LEVEL_DELETE): ?> + getUserAccessLevel('pipelines.removeFromPipeline') >= ACCESS_LEVEL_DELETE): ?> @@ -523,12 +523,12 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; - accessLevel >= ACCESS_LEVEL_EDIT): ?> + getUserAccessLevel('candidates.edit') >= ACCESS_LEVEL_EDIT): ?> - accessLevel >= ACCESS_LEVEL_DELETE): ?> + getUserAccessLevel('candidates.delete') >= ACCESS_LEVEL_DELETE): ?> diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js index 7814cb390..de9aaf257 100644 --- a/modules/candidates/quickAction-duplicates.js +++ b/modules/candidates/quickAction-duplicates.js @@ -9,10 +9,14 @@ quickAction.CandidateDuplicateMenu.prototype = Object.create(quickAction.Default quickAction.CandidateDuplicateMenu.prototype.getOptions = function() { - return [ - new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), - new quickAction.LinkMenuOption('Remove duplicity warning', this.urlDecode(this.removeUrl), 1) - ]; + if(this.getPermissions().candidate_merge) + { + return [ + new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), + new quickAction.LinkMenuOption('Remove duplicity warning', this.urlDecode(this.removeUrl), 1) + ] + } + return null; } quickAction.CandidateDuplicateMenu.prototype.urlDecode = function(url) diff --git a/src/OpenCATS/UI/QuickActionMenu.php b/src/OpenCATS/UI/QuickActionMenu.php index 8c311a971..2070e30b7 100644 --- a/src/OpenCATS/UI/QuickActionMenu.php +++ b/src/OpenCATS/UI/QuickActionMenu.php @@ -27,13 +27,14 @@ protected function getParameters() { $addToPipeline = ($_SESSION['CATS']->getAccessLevel('pipelines.addToPipeline') > ACCESS_LEVEL_READ) ? 1 : 0; $editCandidate = ($_SESSION['CATS']->getAccessLevel('candidates.edit') > ACCESS_LEVEL_READ) ? 1 : 0; + $mergeCandidates = ($_SESSION['CATS']->getAccessLevel('canidates.merge') >= ACCESS_LEVEL_SA) ? 1 : 0; return array( $this->dataItemType, $this->dataItemId, 'docjslib_getRealLeft(this)-20', 'docjslib_getRealTop(this)+20', - '{pipelines_addToPipeline: '.$addToPipeline.'}' + '{pipelines_addToPipeline: '.$addToPipeline.', candidates_merge: '.$mergeCandidates.'}' ); } From 1e8e57190c375f516f3b9ec128f1a6a4b1c18733 Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Sat, 24 Dec 2016 01:41:40 +0100 Subject: [PATCH 11/92] fixed typo error --- modules/candidates/CandidatesUI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index 513f16057..af9a281c6 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -74,7 +74,7 @@ public function __construct() $this->_subTabs = array( 'Add Candidate' => CATSUtility::getIndexName() . '?m=candidates&a=add*al=' . ACCESS_LEVEL_EDIT . '@candidates.add', 'Search Candidates' => CATSUtility::getIndexName() . '?m=candidates&a=search', - 'View Duplicates' => CATSUtility::getIndexName() . '?m=candidates&a=viewDuplicates*al=' . ACCESS_LEVEL_SA . '@canidates.viewDuplicates' + 'View Duplicates' => CATSUtility::getIndexName() . '?m=candidates&a=viewDuplicates*al=' . ACCESS_LEVEL_SA . '@candidates.viewDuplicates' ); } From faf3f1a1cd96a80dbdfe3e305776e0b4fec00548 Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Sat, 24 Dec 2016 23:39:20 +0100 Subject: [PATCH 12/92] fixed typo error --- config.php | 12 ++++++------ modules/candidates/CandidatesUI.php | 14 +++++++------- src/OpenCATS/UI/QuickActionMenu.php | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/config.php b/config.php index 02d95e8a6..762e7637c 100755 --- a/config.php +++ b/config.php @@ -433,12 +433,12 @@ 'candidates.emailCandidates' 'candidates.show_questionnaire' 'candidates.list' - 'canidates.viewDuplicates' - 'canidates.linkDuplicate' - 'canidates.merge' - 'canidates.mergeInfo' - 'canidates.removeDuplicity' - 'canidates.addDuplicates' + 'candidates.viewDuplicates' + 'candidates.linkDuplicate' + 'candidates.merge' + 'candidates.mergeInfo' + 'candidates.removeDuplicity' + 'candidates.addDuplicates' 'calendar.show' 'calendar.addEvent' 'calendar.editEvent' diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index af9a281c6..ee9e5a4ae 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -316,7 +316,7 @@ public function handleRequest() break; case 'viewDuplicates': - if ($this->getUserAccessLevel('canidates.viewDuplicates') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.viewDuplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -324,7 +324,7 @@ public function handleRequest() break; case 'showDuplicate': - if ($this->getUserAccessLevel('canidates.viewDuplicates') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.viewDuplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -333,7 +333,7 @@ public function handleRequest() break; case 'linkDuplicate': - if ($this->getUserAccessLevel('canidates.linkDuplicate') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('s.linkDuplicate') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -342,7 +342,7 @@ public function handleRequest() /* Merge two duplicate candidates into the older one */ case 'merge': - if ($this->getUserAccessLevel('canidates.merge') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.merge') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -350,7 +350,7 @@ public function handleRequest() break; case 'mergeInfo': - if ($this->getUserAccessLevel('canidates.mergeInfo') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.mergeInfo') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -359,7 +359,7 @@ public function handleRequest() /* Remove duplicity warning from a new candidate */ case 'removeDuplicity': - if ($this->getUserAccessLevel('canidates.removeDuplicity') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.removeDuplicity') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -367,7 +367,7 @@ public function handleRequest() break; case 'addDuplicates': - if ($this->getUserAccessLevel('canidates.addDuplicates') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.addDuplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } diff --git a/src/OpenCATS/UI/QuickActionMenu.php b/src/OpenCATS/UI/QuickActionMenu.php index 2070e30b7..f79bddbfa 100644 --- a/src/OpenCATS/UI/QuickActionMenu.php +++ b/src/OpenCATS/UI/QuickActionMenu.php @@ -27,7 +27,7 @@ protected function getParameters() { $addToPipeline = ($_SESSION['CATS']->getAccessLevel('pipelines.addToPipeline') > ACCESS_LEVEL_READ) ? 1 : 0; $editCandidate = ($_SESSION['CATS']->getAccessLevel('candidates.edit') > ACCESS_LEVEL_READ) ? 1 : 0; - $mergeCandidates = ($_SESSION['CATS']->getAccessLevel('canidates.merge') >= ACCESS_LEVEL_SA) ? 1 : 0; + $mergeCandidates = ($_SESSION['CATS']->getAccessLevel('candidates.merge') >= ACCESS_LEVEL_SA) ? 1 : 0; return array( $this->dataItemType, From 8a2bbc368de12ee79e730aa856074e92948e616c Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Tue, 10 Jan 2017 13:20:57 +0100 Subject: [PATCH 13/92] fixed duplicate quick action menu - added permissions and access level --- modules/candidates/ShowDuplicate.tpl | 1 + modules/candidates/quickAction-duplicates.js | 6 +++--- src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index d5cf7be36..2a821d88f 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -38,6 +38,7 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; data['candidateID'], + $_SESSION['CATS']->getAccessLevel('candidates.duplicates'), urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] ))); ?> diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js index de9aaf257..f65a3c9c7 100644 --- a/modules/candidates/quickAction-duplicates.js +++ b/modules/candidates/quickAction-duplicates.js @@ -1,6 +1,6 @@ -quickAction.CandidateDuplicateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, mergeUrl, removeUrl) +quickAction.CandidateDuplicateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, permissions, mergeUrl, removeUrl) { - quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY); + quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY, permissions); this.mergeUrl = mergeUrl; this.removeUrl = removeUrl; } @@ -9,7 +9,7 @@ quickAction.CandidateDuplicateMenu.prototype = Object.create(quickAction.Default quickAction.CandidateDuplicateMenu.prototype.getOptions = function() { - if(this.getPermissions().candidate_merge) + if(this.getPermissions().candidates_merge) { return [ new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), diff --git a/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php index ac7f96716..43abe6c2e 100644 --- a/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php +++ b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php @@ -6,9 +6,9 @@ class CandidateDuplicateQuickActionMenu extends QuickActionMenu private $mergeUrl; private $removeUrl; - public function __construct($dataItemType, $dataItemId, $mergeUrl, $removeUrl) + public function __construct($dataItemType, $dataItemId, $accessLevel, $mergeUrl, $removeUrl) { - parent::__construct($dataItemType, $dataItemId); + parent::__construct($dataItemType, $dataItemId, $accessLevel); $this->mergeUrl = $mergeUrl; $this->removeUrl = $removeUrl; } From 9ab60c2a8e0c261c4a1722078b2c182b31769a6d Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Tue, 10 Jan 2017 13:20:57 +0100 Subject: [PATCH 14/92] fixed duplicate quick action menu - added permissions and access level --- modules/candidates/ShowDuplicate.tpl | 1 + modules/candidates/quickAction-duplicates.js | 6 +++--- src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl index d5cf7be36..2a821d88f 100644 --- a/modules/candidates/ShowDuplicate.tpl +++ b/modules/candidates/ShowDuplicate.tpl @@ -38,6 +38,7 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; data['candidateID'], + $_SESSION['CATS']->getAccessLevel('candidates.duplicates'), urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] ))); ?> diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js index de9aaf257..f65a3c9c7 100644 --- a/modules/candidates/quickAction-duplicates.js +++ b/modules/candidates/quickAction-duplicates.js @@ -1,6 +1,6 @@ -quickAction.CandidateDuplicateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, mergeUrl, removeUrl) +quickAction.CandidateDuplicateMenu = function(menuDataItemType, menuDataItemId, menuX, menuY, permissions, mergeUrl, removeUrl) { - quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY); + quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY, permissions); this.mergeUrl = mergeUrl; this.removeUrl = removeUrl; } @@ -9,7 +9,7 @@ quickAction.CandidateDuplicateMenu.prototype = Object.create(quickAction.Default quickAction.CandidateDuplicateMenu.prototype.getOptions = function() { - if(this.getPermissions().candidate_merge) + if(this.getPermissions().candidates_merge) { return [ new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), diff --git a/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php index ac7f96716..43abe6c2e 100644 --- a/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php +++ b/src/OpenCATS/UI/CandidateDuplicateQuickActionMenu.php @@ -6,9 +6,9 @@ class CandidateDuplicateQuickActionMenu extends QuickActionMenu private $mergeUrl; private $removeUrl; - public function __construct($dataItemType, $dataItemId, $mergeUrl, $removeUrl) + public function __construct($dataItemType, $dataItemId, $accessLevel, $mergeUrl, $removeUrl) { - parent::__construct($dataItemType, $dataItemId); + parent::__construct($dataItemType, $dataItemId, $accessLevel); $this->mergeUrl = $mergeUrl; $this->removeUrl = $removeUrl; } From 5531084579335c10082bb26f2b9dbfb11f946a2e Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 13:52:46 +0100 Subject: [PATCH 15/92] removing duplicate info from function parameters --- lib/Candidates.php | 2 +- lib/DataGrid.php | 4 ++-- modules/candidates/CandidatesUI.php | 2 +- modules/candidates/dataGrids.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Candidates.php b/lib/Candidates.php index 7c60a1380..fca97c008 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -1674,7 +1674,7 @@ class CandidatesDataGrid extends DataGrid protected $_siteID; // FIXME: Fix ugly indenting - ~400 character lines = bad. - public function __construct($instanceName, $siteID, $parameters, $misc = 0, $duplicates = 0) + public function __construct($instanceName, $siteID, $parameters, $misc = 0) { $this->_db = DatabaseConnection::getInstance(); $this->_siteID = $siteID; diff --git a/lib/DataGrid.php b/lib/DataGrid.php index 743922650..3f82e7d9d 100755 --- a/lib/DataGrid.php +++ b/lib/DataGrid.php @@ -240,7 +240,7 @@ class DataGrid * @param integer dataGrid miscalaneous ID * @return object data grid */ - public static function get($indentifier, $parameters, $misc = 0, $duplicates = 0) + public static function get($indentifier, $parameters, $misc = 0) { /* This deals with loading a datagrid that was selected by use of the action / export box. */ if (isset($_REQUEST['dynamicArgument' . md5($indentifier)])) @@ -279,7 +279,7 @@ public static function get($indentifier, $parameters, $misc = 0, $duplicates = 0 include_once (sprintf('modules/%s/dataGrids.php', $module)); - $dg = new $class($_SESSION['CATS']->getSiteID(), $parameters, $misc, $duplicates); + $dg = new $class($_SESSION['CATS']->getSiteID(), $parameters, $misc); return $dg; } diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index e7d026e3d..f6952808a 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -473,7 +473,7 @@ private function listByView($errMessage = '') /* * Called by handleRequest() to process loading the details page. */ - private function show($duplicate = 0) + private function show() { /* Is this a popup? */ if (isset($_GET['display']) && $_GET['display'] == 'popup') diff --git a/modules/candidates/dataGrids.php b/modules/candidates/dataGrids.php index ece658894..cb5e87a68 100755 --- a/modules/candidates/dataGrids.php +++ b/modules/candidates/dataGrids.php @@ -7,7 +7,7 @@ class candidatesListByViewDataGrid extends CandidatesDataGrid { - public function __construct($siteID, $parameters, $misc, $duplicates = 0) + public function __construct($siteID, $parameters, $misc) { /* Pager configuration. */ $this->_tableWidth = new Width(100, '%'); @@ -34,7 +34,7 @@ public function __construct($siteID, $parameters, $misc, $duplicates = 0) ); parent::__construct("candidates:candidatesListByViewDataGrid", - $siteID, $parameters, $misc, $duplicates + $siteID, $parameters, $misc ); } From 03ab64e5728d2bc1bdba3bfd2a26c53e0192c827 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:03:28 +0100 Subject: [PATCH 16/92] adjusting candidate datagrid --- lib/Candidates.php | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/Candidates.php b/lib/Candidates.php index fca97c008..adf679eb8 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -1683,16 +1683,26 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) $this->_classColumns = array( 'Attachments' => array('select' => 'IF(candidate_joborder_submitted.candidate_joborder_id, 1, 0) AS submitted, - IF(attachment_id, 1, 0) AS attachmentPresent', + IF(attachment_id, 1, 0) AS attachmentPresent, + IF(old_candidate_id, 1, 0) AS duplicatePresent', - 'pagerRender' => 'if ($rsData[\'submitted\'] == 1) + 'pagerRender' => 'if ($rsData[\'duplicatePresent\'] == 1 && $_SESSION[\'CATS\']->getAccessLevel(\'candidates.viewDuplicates\') >= ACCESS_LEVEL_SA) { - $return = \'\'; + $return = \'\'; } else { $return = \'\'; } + + if ($rsData[\'submitted\'] == 1) + { + $return .= \'\'; + } + else + { + $return .= \'\'; + } if ($rsData[\'attachmentPresent\'] == 1) { @@ -1712,13 +1722,15 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) ON candidate_joborder_submitted.candidate_id = candidate.candidate_id AND candidate_joborder_submitted.status >= '.PIPELINE_STATUS_SUBMITTED.' AND candidate_joborder_submitted.site_id = '.$this->_siteID.' - AND candidate_joborder_submitted.status != '.PIPELINE_STATUS_NOTINCONSIDERATION + AND candidate_joborder_submitted.status != '.PIPELINE_STATUS_NOTINCONSIDERATION.' LEFT JOIN candidate_duplicates + ON candidate.candidate_id = + candidate_duplicates.new_candidate_id' , - 'pagerWidth' => 34, + 'pagerWidth' => 100, 'pagerOptional' => true, 'pagerNoTitle' => true, - 'sizable' => false, + 'sizable' => true, 'exportable' => false, 'filterable' => false), @@ -1985,16 +1997,6 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) ') ); - if($duplicates == 1) - { - $this->_classColumns['Attachments']['join'] .= ' INNER JOIN candidate_duplicates - ON candidate.candidate_id = - candidate_duplicates.new_candidate_id'; - $this->_classColumns['Attachments']['pagerWidth'] = 70; - $this->_classColumns['First Name']['pagerRender'] = 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'firstName\']).\'\';'; - $this->_classColumns['Last Name']['pagerRender'] = 'if ($rsData[\'isHot\'] == 1) $className = \'jobLinkHot\'; else $className = \'jobLinkCold\'; return \'\'.htmlspecialchars($rsData[\'lastName\']).\'\';'; - } - if (US_ZIPS_ENABLED) { $this->_classColumns['Near Zipcode'] = From ddebc293e28ec241213a539f7011fdf11d14baa0 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:04:12 +0100 Subject: [PATCH 17/92] removing unnecessary duplicate code --- modules/candidates/CandidatesUI.php | 82 +++-------------------------- 1 file changed, 7 insertions(+), 75 deletions(-) diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index f6952808a..d2495c3e5 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -73,8 +73,7 @@ public function __construct() $this->_moduleTabText = 'Candidates'; $this->_subTabs = array( 'Add Candidate' => CATSUtility::getIndexName() . '?m=candidates&a=add*al=' . ACCESS_LEVEL_EDIT . '@candidates.add', - 'Search Candidates' => CATSUtility::getIndexName() . '?m=candidates&a=search', - 'View Duplicates' => CATSUtility::getIndexName() . '?m=candidates&a=viewDuplicates*al=' . ACCESS_LEVEL_SA . '@candidates.viewDuplicates' + 'Search Candidates' => CATSUtility::getIndexName() . '?m=candidates&a=search' ); } @@ -315,25 +314,8 @@ public function handleRequest() $this->onShowQuestionnaire(); break; - case 'viewDuplicates': - if ($this->getUserAccessLevel('candidates.viewDuplicates') < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } - $this->showDuplicates(); - break; - - case 'showDuplicate': - if ($this->getUserAccessLevel('candidates.viewDuplicates') < ACCESS_LEVEL_SA) - { - CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); - } - $duplicate = 1; - $this->show($duplicate); - break; - case 'linkDuplicate': - if ($this->getUserAccessLevel('s.linkDuplicate') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.linkDuplicate') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -502,14 +484,7 @@ private function show() $candidateID = $candidates->getIDByEmail($_GET['email']); } - if($duplicate == 0) - { - $data = $candidates->get($candidateID); - } - else - { - $data = $candidates->getWithDuplicity($candidateID); - } + $data = $candidates->getWithDuplicity($candidateID); /* Bail out if we got an empty result set. */ if (empty($data)) @@ -756,17 +731,10 @@ private function show() $this->_template->assign('sessionCookie', $_SESSION['CATS']->getCookie()); $this->_template->assign('tagsRS', $tags->getAll()); $this->_template->assign('assignedTags', $tags->getCandidateTagsTitle($candidateID)); - $this->_template->assign('isDuplicate', 1); - + + $this->_template->display('./modules/candidates/Show.tpl'); + if (!eval(Hooks::get('CANDIDATE_SHOW'))) return; - if($duplicate == 0) - { - $this->_template->display('./modules/candidates/Show.tpl'); - } - else - { - $this->_template->display('./modules/candidates/ShowDuplicate.tpl'); - } } @@ -3374,42 +3342,6 @@ private function onShowQuestionnaire() $this->_template->display('./modules/candidates/Questionnaire.tpl'); } - private function showDuplicates($errMessage = '') - { - // Log message that shows up on the top of the list page - $topLog = ''; - - $dataGridProperties = DataGrid::getRecentParamaters("candidates:candidatesListByViewDataGrid"); - - /* If this is the first time we visited the datagrid this session, the recent paramaters will - * be empty. Fill in some default values. */ - if ($dataGridProperties == array()) - { - $dataGridProperties = array('rangeStart' => 0, - 'maxResults' => 15, - 'filterVisible' => false); - } - - $tags = new Tags($this->_siteID); - $tagsRS = $tags->getAll(); - - $dataGrid = DataGrid::get("candidates:candidatesListByViewDataGrid", $dataGridProperties, 0, 1); - - $candidates = new Candidates($this->_siteID); - $this->_template->assign('totalDuplicates', $candidates->getDuplicatesCount()); - - $this->_template->assign('active', $this); - $this->_template->assign('dataGrid', $dataGrid); - $this->_template->assign('userID', $_SESSION['CATS']->getUserID()); - $this->_template->assign('errMessage', $errMessage); - $this->_template->assign('topLog', $topLog); - $this->_template->assign('tagsRS', $tagsRS); - - if (!eval(Hooks::get('DUPLICATE_LIST_BY_VIEW'))) return; - - $this->_template->display('./modules/candidates/Duplicates.tpl'); - } - private function findDuplicateCandidateSearch() { $duplicateCandidateID = $_GET['candidateID']; @@ -3519,7 +3451,7 @@ private function removeDuplicity() $oldCandidateID = $_GET['oldCandidateID']; $newCandidateID = $_GET['newCandidateID']; $candidates->removeDuplicity($oldCandidateID, $newCandidateID); - $url = CATSUtility::getIndexName()."?m=candidates&a=viewDuplicates"; + $url = CATSUtility::getIndexName()."?m=candidates"; header("Location: " . $url); /* Redirect browser */ exit(); } From 631d5e043f3571ef9a2bfdeb712a02bb1736f25f Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:04:21 +0100 Subject: [PATCH 18/92] adding includes --- modules/candidates/Show.tpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index 2dd12cb46..c3d3edd9e 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -1,11 +1,12 @@ isPopup): ?> data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-candidates.js')); ?> - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-candidates.js')); ?> + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-candidates.js', 'modules/candidates/quickAction-duplicates.js')); ?> active); ?> From 4d4104a661e4ccb4958dc573600937cc40835f43 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:05:04 +0100 Subject: [PATCH 19/92] added duplicate info into candidate show page --- modules/candidates/Show.tpl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index c3d3edd9e..a0cd52d1f 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -20,7 +20,23 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; Candidates 

Candidates: Candidate Details

Candidates: Candidate Details + getAccessLevel('candidates.duplicates') >= ACCESS_LEVEL_SA): ?> + data['isDuplicate'])): ?> + duplicate_warning + data['isDuplicate'] as $item): ?> + Duplicate' ?> + data['candidateID'], + $_SESSION['CATS']->getAccessLevel('candidates.duplicates'), + urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), + urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] + ))); ?> + + + +

From d35de07d0e77e428e1d486232626d771122faeee Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:13:19 +0100 Subject: [PATCH 20/92] config update --- config.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/config.php b/config.php index 762e7637c..9ebcac0b9 100755 --- a/config.php +++ b/config.php @@ -433,12 +433,7 @@ 'candidates.emailCandidates' 'candidates.show_questionnaire' 'candidates.list' - 'candidates.viewDuplicates' - 'candidates.linkDuplicate' - 'candidates.merge' - 'candidates.mergeInfo' - 'candidates.removeDuplicity' - 'candidates.addDuplicates' + 'candidates.duplicates' 'calendar.show' 'calendar.addEvent' 'calendar.editEvent' From cf3f5e6769c73e8d3c736dba6d72c3588d223477 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:14:18 +0100 Subject: [PATCH 21/92] changed duplicate secure object names --- lib/Candidates.php | 2 +- modules/candidates/CandidatesUI.php | 10 +++++----- modules/candidates/Show.tpl | 2 +- modules/candidates/dataGrids.php | 2 +- src/OpenCATS/UI/QuickActionMenu.php | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Candidates.php b/lib/Candidates.php index adf679eb8..451e6b384 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -1686,7 +1686,7 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) IF(attachment_id, 1, 0) AS attachmentPresent, IF(old_candidate_id, 1, 0) AS duplicatePresent', - 'pagerRender' => 'if ($rsData[\'duplicatePresent\'] == 1 && $_SESSION[\'CATS\']->getAccessLevel(\'candidates.viewDuplicates\') >= ACCESS_LEVEL_SA) + 'pagerRender' => 'if ($rsData[\'duplicatePresent\'] == 1 && $_SESSION[\'CATS\']->getAccessLevel(\'candidates.duplicates\') >= ACCESS_LEVEL_SA) { $return = \'\'; } diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index d2495c3e5..de38eabd8 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -315,7 +315,7 @@ public function handleRequest() break; case 'linkDuplicate': - if ($this->getUserAccessLevel('candidates.linkDuplicate') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.duplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -324,7 +324,7 @@ public function handleRequest() /* Merge two duplicate candidates into the older one */ case 'merge': - if ($this->getUserAccessLevel('candidates.merge') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.duplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -332,7 +332,7 @@ public function handleRequest() break; case 'mergeInfo': - if ($this->getUserAccessLevel('candidates.mergeInfo') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.duplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -341,7 +341,7 @@ public function handleRequest() /* Remove duplicity warning from a new candidate */ case 'removeDuplicity': - if ($this->getUserAccessLevel('candidates.removeDuplicity') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.duplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } @@ -349,7 +349,7 @@ public function handleRequest() break; case 'addDuplicates': - if ($this->getUserAccessLevel('candidates.addDuplicates') < ACCESS_LEVEL_SA) + if ($this->getUserAccessLevel('candidates.duplicates') < ACCESS_LEVEL_SA) { CommonErrors::fatal(COMMONERROR_PERMISSION, $this, 'Invalid user level for action.'); } diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index a0cd52d1f..69356dfe6 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -452,7 +452,7 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu;      - getUserAccessLevel('candidates.linkDuplicate') >= ACCESS_LEVEL_SA): ?> + getUserAccessLevel('candidates.duplicates') >= ACCESS_LEVEL_SA): ?> add duplicate Link duplicate diff --git a/modules/candidates/dataGrids.php b/modules/candidates/dataGrids.php index cb5e87a68..4a82ba06f 100755 --- a/modules/candidates/dataGrids.php +++ b/modules/candidates/dataGrids.php @@ -60,7 +60,7 @@ public function getInnerActionArea() $html .= $this->getInnerActionAreaItemPopup('Add To Pipeline', CATSUtility::getIndexName().'?m=candidates&a=considerForJobSearch', 750, 460); } - if(MAIL_MAILER != 0 && $_SESSION['CATS']->getAccessLevel('candidates.canEmail') >= ACCESS_LEVEL_SA) + if(MAIL_MAILER != 0 && $_SESSION['CATS']->getAccessLevel('candidates.emailCandidates') >= ACCESS_LEVEL_SA) { $html .= $this->getInnerActionAreaItem('Send E-Mail', CATSUtility::getIndexName().'?m=candidates&a=emailCandidates'); } diff --git a/src/OpenCATS/UI/QuickActionMenu.php b/src/OpenCATS/UI/QuickActionMenu.php index f79bddbfa..704818502 100644 --- a/src/OpenCATS/UI/QuickActionMenu.php +++ b/src/OpenCATS/UI/QuickActionMenu.php @@ -27,7 +27,7 @@ protected function getParameters() { $addToPipeline = ($_SESSION['CATS']->getAccessLevel('pipelines.addToPipeline') > ACCESS_LEVEL_READ) ? 1 : 0; $editCandidate = ($_SESSION['CATS']->getAccessLevel('candidates.edit') > ACCESS_LEVEL_READ) ? 1 : 0; - $mergeCandidates = ($_SESSION['CATS']->getAccessLevel('candidates.merge') >= ACCESS_LEVEL_SA) ? 1 : 0; + $mergeCandidates = ($_SESSION['CATS']->getAccessLevel('candidates.duplicates') >= ACCESS_LEVEL_SA) ? 1 : 0; return array( $this->dataItemType, From afe466927dccfdf7522555359f884730b0b84063 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:42:24 +0100 Subject: [PATCH 22/92] removed unused template --- modules/candidates/ShowDuplicate.tpl | 554 --------------------------- 1 file changed, 554 deletions(-) delete mode 100644 modules/candidates/ShowDuplicate.tpl diff --git a/modules/candidates/ShowDuplicate.tpl b/modules/candidates/ShowDuplicate.tpl deleted file mode 100644 index 2a821d88f..000000000 --- a/modules/candidates/ShowDuplicate.tpl +++ /dev/null @@ -1,554 +0,0 @@ - -isPopup): ?> - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-duplicates.js')); ?> - - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-duplicates.js')); ?> - - - active); ?> -
- - - -
- - - - - -
- Candidates  -

Duplicates: Duplicate Details - data['isDuplicate'])): ?> - duplicate_warning - data['isDuplicate'] as $item): ?> - Duplicate' ?> - data['candidateID'], - $_SESSION['CATS']->getAccessLevel('candidates.duplicates'), - urlencode(CATSUtility::getIndexName().'?m=candidates&a=merge&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID']), - urlencode(CATSUtility::getIndexName().'?m=candidates&a=removeDuplicity&oldCandidateID='.$item['duplicateTo'].'&newCandidateID='.$this->data['candidateID'] - ))); ?> - - -

- -

Duplicate Details

- - data['isAdminHidden'] == 1): ?> -

This Candidate is hidden. Only CATS Administrators can view it or search for it. To make it visible by the site users, click Here.

- - - - - - attachmentsRS as $rowNumber => $attachmentsData): ?> - - - - - - - - - - attachmentsRS as $rowNumber => $attachmentsData): ?> - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - extraFieldRS)/2); $i++): ?> - - - - - - - - - - -
Name: - - - _($this->data['firstName']); ?> - _($this->data['middleName']); ?> - _($this->data['lastName']); ?> - data['isActive'] != 1): ?> -  (INACTIVE) - - -
E-Mail: - - _($this->data['email1']); ?> - -
2nd E-Mail: - - _($this->data['email2']); ?> - -
Home Phone:_($this->data['phoneHome']); ?>
Cell Phone:_($this->data['phoneCell']); ?>
Work Phone:_($this->data['phoneWork']); ?>
Best Time To Call:_($this->data['bestTimeToCall']); ?>
Address:data['address']))); ?>
  - _($this->data['cityAndState']); ?> - _($this->data['zip']); ?> -
Web Site: - data['webSite'])): ?> - _($this->data['webSite']); ?> - -
Source:_($this->data['source']); ?>
_($this->extraFieldRS[$i]['fieldName']); ?>:extraFieldRS[$i]['display']); ?>
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - extraFieldRS))/2); $i < (count($this->extraFieldRS)); $i++): ?> - - - - - -
Date Available:_($this->data['dateAvailable']); ?>
Current Employer:_($this->data['currentEmployer']); ?>
Key Skills:_($this->data['keySkills']); ?>
Can Relocate:_($this->data['canRelocate']); ?>
Current Pay:_($this->data['currentPay']); ?>
Desired Pay:_($this->data['desiredPay']); ?>
Pipeline:_($this->data['pipeline']); ?>
Submitted:_($this->data['submitted']); ?>
Created:_($this->data['dateCreated']); ?> (_($this->data['enteredByFullName']); ?>)
Owner:_($this->data['ownerFullName']); ?>
_($this->extraFieldRS[$i]['fieldName']); ?>:extraFieldRS[$i]['display']); ?>
-
- - - - - - - -
- isPopup): ?> - getUserAccessLevel('candidates.deleteAttachment') >= ACCESS_LEVEL_DELETE): ?> - - - - - -      -    - Picture:       -
- - - -
-
- - EEOSettingsRS['enabled'] == 1): ?> - - - - - - -
- - EEOValues)/2); $i++): ?> - - - EEOSettingsRS['canSeeEEOInfo']): ?> - - - - - - -
_($this->EEOValues[$i]['fieldName']); ?>:_($this->EEOValues[$i]['fieldValue']); ?>(Hidden)
-
- - - - - EEOValues))/2); $i < intval(count($this->EEOValues)); $i++): ?> - - - EEOSettingsRS['canSeeEEOInfo']): ?> - - - - - - -
_($this->EEOValues[$i]['fieldName']); ?>:_($this->EEOValues[$i]['fieldValue']); ?>(Hidden)
-
- - - - - - -
- - - - isShortNotes): ?> - - - - - - - - - - - - - questionnaires) && !empty($this->questionnaires)): ?> - - - - - - - - - - - - - - -
Misc. Notes: - data['shortNotes']); ?>...  -

[More]

-
- data['notes']); ?> -
Upcoming Events: - calendarRS as $rowNumber => $calendarData): ?> - - - -
Questionnaires: - - - - - - - questionnaires as $questionnaire): ?> - - - - - - - -
Title (Internal)CompletedDescription (Public)
- - view View - -   - - print Print - -
-
Attachments: - - attachmentsRS as $rowNumber => $attachmentsData): ?> - - - - - - - - -
- - -   - _($attachmentsData['originalFilename']) ?> - - _($attachmentsData['dateCreated']) ?>
-
Tags: - - assignedTags) ?> -
-
-isPopup): ?> - privledgedUser): ?> - -  View History - -      - - -
-
- -

Job Order Pipeline

- - - - - - - - - - -isPopup): ?> - - - - - pipelinesRS as $rowNumber => $pipelinesData): ?> - - - - - - - - - -isPopup): ?> - - - - - - - - -
MatchTitleCompanyOwnerAddedEntered ByStatusAction
- - - - - - - - - - - _($pipelinesData['title']) ?> - - - - _($pipelinesData['companyName']) ?> - - _($pipelinesData['ownerAbbrName']) ?>_($pipelinesData['dateCreated']) ?>_($pipelinesData['addedByAbbrName']) ?>_($pipelinesData['status']) ?> - - getUserAccessLevel('pipelines.screening') >= ACCESS_LEVEL_EDIT && !$_SESSION['CATS']->hasUserCategory('sourcer')): ?> - - - - - - - - - getUserAccessLevel('pipelines.addActivityChangeStatus') >= ACCESS_LEVEL_EDIT): ?> - - - - - getUserAccessLevel('pipelines.removeFromPipeline') >= ACCESS_LEVEL_DELETE): ?> - - - - -
- -
-
- -

Activity

- - - - - - - - -isPopup): ?> - - - - - activityRS as $rowNumber => $activityData): ?> - - - - - - -isPopup): ?> - - - - -
DateTypeEnteredRegardingNotesAction
_($activityData['dateCreated']) ?>_($activityData['typeDescription']) ?>_($activityData['enteredByAbbrName']) ?>_($activityData['regarding']) ?> - getUserAccessLevel('candidates.edit') >= ACCESS_LEVEL_EDIT): ?> - - - - - getUserAccessLevel('candidates.delete') >= ACCESS_LEVEL_DELETE): ?> - - - - -
-isPopup): ?> -
- -
-
-
- - - - - -
- From bac70c183f982e195ed075fca506bfe237509bcf Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 6 Feb 2017 14:59:15 +0100 Subject: [PATCH 23/92] db upgrade, not tested --- db/upgrade-0.9.2-0.9.3.sql | 6 ++++++ modules/install/ajax/ui.php | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 db/upgrade-0.9.2-0.9.3.sql diff --git a/db/upgrade-0.9.2-0.9.3.sql b/db/upgrade-0.9.2-0.9.3.sql new file mode 100644 index 000000000..acc0a47cf --- /dev/null +++ b/db/upgrade-0.9.2-0.9.3.sql @@ -0,0 +1,6 @@ +CREATE TABLE `candidate_duplicates` ( + `old_candidate_id` int(11) NOT NULL, + `new_candidate_id` int(11) NOT NULL, + `site_id` int(11) NOT NULL, + PRIMARY KEY (`old_candidate_id`, `new_candidate_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; diff --git a/modules/install/ajax/ui.php b/modules/install/ajax/ui.php index 435bcd837..770da393d 100755 --- a/modules/install/ajax/ui.php +++ b/modules/install/ajax/ui.php @@ -874,10 +874,15 @@ /* 0.6.0 */ $revision = 60; } - else if (isset($tables['history'])) + else if (!isset($tables['candidate_duplicates'])) + { + /* 0.9.2 */ + $revision = 92; + } + else if (isset($tables['candidate_duplicates'])) { - /* 0.7.0 */ - $revision = 70; + /* 0.9.3 */ + $revision = 93; } if ($revision <= 50) @@ -904,6 +909,12 @@ $schema = file_get_contents('db/upgrade-0.6.x-0.7.0.sql'); MySQLQueryMultiple($schema); } + if ($revision <= 92) + { + // FIXME: File exists?! + $schema = file_get_contents('db/upgrade-0.9.2-0.9.3.sql'); + MySQLQueryMultiple($schema); + } // FIXME: File exists?! $schema = @file_get_contents('db/upgrade-zipcodes.sql'); From 5a90cbb383f86c950af22caa3f3a365dccf5244e Mon Sep 17 00:00:00 2001 From: skrchnavy Date: Thu, 30 Mar 2017 12:38:25 +0200 Subject: [PATCH 24/92] updated migration from 0.9.4 to 0.9.5 --- ...grade-0.9.2-0.9.3.sql => upgrade-0.9.4-0.9.5.sql} | 0 modules/install/ajax/ui.php | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) rename db/{upgrade-0.9.2-0.9.3.sql => upgrade-0.9.4-0.9.5.sql} (100%) diff --git a/db/upgrade-0.9.2-0.9.3.sql b/db/upgrade-0.9.4-0.9.5.sql similarity index 100% rename from db/upgrade-0.9.2-0.9.3.sql rename to db/upgrade-0.9.4-0.9.5.sql diff --git a/modules/install/ajax/ui.php b/modules/install/ajax/ui.php index 770da393d..9535c668f 100755 --- a/modules/install/ajax/ui.php +++ b/modules/install/ajax/ui.php @@ -876,13 +876,13 @@ } else if (!isset($tables['candidate_duplicates'])) { - /* 0.9.2 */ - $revision = 92; + /* 0.9.4 */ + $revision = 94; } else if (isset($tables['candidate_duplicates'])) { - /* 0.9.3 */ - $revision = 93; + /* 0.9.5 */ + $revision = 95; } if ($revision <= 50) @@ -909,10 +909,10 @@ $schema = file_get_contents('db/upgrade-0.6.x-0.7.0.sql'); MySQLQueryMultiple($schema); } - if ($revision <= 92) + if ($revision <= 94) { // FIXME: File exists?! - $schema = file_get_contents('db/upgrade-0.9.2-0.9.3.sql'); + $schema = file_get_contents('db/upgrade-0.9.4-0.9.5.sql'); MySQLQueryMultiple($schema); } From 5378b0d609d246e2ce93a607cb192fd5358d4435 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Tue, 28 Mar 2017 14:13:50 +0200 Subject: [PATCH 25/92] added duplicate db table to test db --- test/data/test.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/data/test.sql b/test/data/test.sql index ccbca96db..578d9bd01 100644 --- a/test/data/test.sql +++ b/test/data/test.sql @@ -287,6 +287,18 @@ LOCK TABLES `candidate` WRITE; /*!40000 ALTER TABLE `candidate` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `candidate_duplicates` */ +-- + +DROP TABLE IF EXISTS `candidate_duplicates`; +CREATE TABLE `candidate_duplicates` ( + `old_candidate_id` int(11) NOT NULL, + `new_candidate_id` int(11) NOT NULL, + `site_id` int(11) NOT NULL, + PRIMARY KEY (`old_candidate_id`, `new_candidate_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + -- -- Table structure for table `candidate_joborder` -- From 0db9988d7bd00c2f07f9b2b489f3f44acf490fc5 Mon Sep 17 00:00:00 2001 From: skrchnavy Date: Thu, 30 Mar 2017 17:26:42 +0200 Subject: [PATCH 26/92] correction in javascript based on codacy review --- lib/Candidates.php | 2 +- modules/candidates/quickAction-duplicates.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Candidates.php b/lib/Candidates.php index 451e6b384..79ed13b77 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -1438,7 +1438,7 @@ public function mergeDuplicates($params, $rs) $this->_db->query($sql); - $sql = sprintf( + $sql = sprintf( "UPDATE candidate_tag SET diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js index f65a3c9c7..384eebd84 100644 --- a/modules/candidates/quickAction-duplicates.js +++ b/modules/candidates/quickAction-duplicates.js @@ -3,7 +3,7 @@ quickAction.CandidateDuplicateMenu = function(menuDataItemType, menuDataItemId, quickAction.DefaultMenu.call(this, menuDataItemType, menuDataItemId, menuX, menuY, permissions); this.mergeUrl = mergeUrl; this.removeUrl = removeUrl; -} +}; quickAction.CandidateDuplicateMenu.prototype = Object.create(quickAction.DefaultMenu.prototype); @@ -12,14 +12,14 @@ quickAction.CandidateDuplicateMenu.prototype.getOptions = function() if(this.getPermissions().candidates_merge) { return [ - new quickAction.LinkMenuOption('Merge', this.urlDecode(this.mergeUrl), 0), - new quickAction.LinkMenuOption('Remove duplicity warning', this.urlDecode(this.removeUrl), 1) - ] + new quickAction.LinkMenuOption("Merge", this.urlDecode(this.mergeUrl), 0), + new quickAction.LinkMenuOption("Remove duplicity warning", this.urlDecode(this.removeUrl), 1) + ]; } return null; } quickAction.CandidateDuplicateMenu.prototype.urlDecode = function(url) { - return decodeURIComponent(url.replace(/\+/g, ' ')); + return decodeURIComponent(url.replace(/\+/g, " ")); } From 03bb9d811bb56028e359e249260372714d47b638 Mon Sep 17 00:00:00 2001 From: skrchnavy Date: Thu, 30 Mar 2017 20:01:07 +0200 Subject: [PATCH 27/92] js code fixes based on codacy review --- modules/candidates/quickAction-duplicates.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/candidates/quickAction-duplicates.js b/modules/candidates/quickAction-duplicates.js index 384eebd84..4e6221496 100644 --- a/modules/candidates/quickAction-duplicates.js +++ b/modules/candidates/quickAction-duplicates.js @@ -17,9 +17,9 @@ quickAction.CandidateDuplicateMenu.prototype.getOptions = function() ]; } return null; -} +}; quickAction.CandidateDuplicateMenu.prototype.urlDecode = function(url) { return decodeURIComponent(url.replace(/\+/g, " ")); -} +}; From d9181ddc8ec3bbd774f6c1f20581a4208c418e10 Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Mon, 3 Apr 2017 07:40:38 +0200 Subject: [PATCH 28/92] removed forgotten or badly merged parameter from a function --- lib/Candidates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Candidates.php b/lib/Candidates.php index 79ed13b77..8458c0105 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -2025,7 +2025,7 @@ public function __construct($instanceName, $siteID, $parameters, $misc = 0) } } - parent::__construct($instanceName, $parameters, $misc, $duplicates); + parent::__construct($instanceName, $parameters, $misc); } /** From b9f833ad494808bc5921985246404dadcba60e6b Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Thu, 20 Apr 2017 14:18:34 +0200 Subject: [PATCH 29/92] duplicate initialization --- modules/candidates/CandidatesUI.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/candidates/CandidatesUI.php b/modules/candidates/CandidatesUI.php index f373620c1..0fa6c0ec5 100755 --- a/modules/candidates/CandidatesUI.php +++ b/modules/candidates/CandidatesUI.php @@ -2653,7 +2653,6 @@ private function _addCandidate($isModal, $directoryOverride = '') return $candidateID; } - $candidates = new Candidates($this->_siteID); if(sizeof($duplicatesID) > 0) { $candidates->addDuplicates($candidateID, $duplicatesID); From d61aff1d10994a3dff4d0af520bfd242a39cb37b Mon Sep 17 00:00:00 2001 From: Kristina Odziomkova Date: Thu, 27 Apr 2017 15:03:48 +0200 Subject: [PATCH 30/92] fixed (hopefully well) duplicate primary key errors during update by using delete and insert ignore --- lib/Candidates.php | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/Candidates.php b/lib/Candidates.php index 8458c0105..65eea3427 100755 --- a/lib/Candidates.php +++ b/lib/Candidates.php @@ -1392,19 +1392,48 @@ public function mergeDuplicates($params, $rs) ); $this->_db->query($sql); - + $sql = sprintf( - "UPDATE + "SELECT + new_candidate_id AS newID + FROM candidate_duplicates - SET - old_candidate_id = %s WHERE - old_candidate_id = %s", - $this->_db->makeQueryInteger($oldCandidateID), + old_candidate_id = %s + ", $this->_db->makeQueryInteger($newCandidateID) ); - - $this->_db->query($sql); + + $rsTmp = $this->_db->getAllAssoc($sql); + + if($rsTmp || count($rsTmp) > 0){ + foreach($rsTmp AS $index => $newID){ + $sql = sprintf( + "DELETE FROM + candidate_duplicates + WHERE + new_candidate_id = %s + AND + old_candidate_id = %s", + $this->_db->makeQueryInteger($newID['newID']), + $this->_db->makeQueryInteger($newCandidateID) + ); + + $this->_db->query($sql); + + $sql = sprintf( + "INSERT IGNORE INTO + candidate_duplicates(old_candidate_id, new_candidate_id, site_id) + VALUES + (%s, %s, %s)", + $this->_db->makeQueryInteger($oldCandidateID), + $this->_db->makeQueryInteger($newID['newID']), + $this->_siteID + ); + + $this->_db->query($sql); + } + } $sql = sprintf( "UPDATE From 6bd26ee8142b2adb80026a527299e8a49a9a377c Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Mon, 1 May 2017 11:50:49 +0200 Subject: [PATCH 31/92] add ckeditor to composer --- composer.json | 3 ++- composer.lock | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index de9a7b116..0cbf5d173 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "behat/mink": "^1.7", "behat/mink-extension": "dev-master", "behat/mink-goutte-driver": "^1.2", - "behat/mink-selenium2-driver": "^1.3" + "behat/mink-selenium2-driver": "^1.3", + "ckeditor/ckeditor": "4.5.11" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 05643ee8f..cf3add4a6 100644 --- a/composer.lock +++ b/composer.lock @@ -454,6 +454,10 @@ "require": { "php": ">=5.3.3" }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0" + }, "type": "library", "extra": { "branch-alias": { @@ -477,6 +481,47 @@ ], "time": "2015-09-28 16:26:35" }, + { + "name": "ckeditor/ckeditor", + "version": "4.5.11", + "source": { + "type": "git", + "url": "https://github.com/ckeditor/ckeditor-releases.git", + "reference": "48155a1e1c7e84736b5a166ad3f33acea2a51255" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/48155a1e1c7e84736b5a166ad3f33acea2a51255", + "reference": "48155a1e1c7e84736b5a166ad3f33acea2a51255", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+", + "LGPL-2.1+", + "MPL-1.1+" + ], + "authors": [ + { + "name": "CKSource", + "homepage": "http://cksource.com" + } + ], + "description": "JavaScript WYSIWYG web text editor.", + "homepage": "http://ckeditor.com", + "keywords": [ + "CKEditor", + "editor", + "fckeditor", + "html", + "javascript", + "richtext", + "text", + "wysiwyg" + ], + "time": "2016-09-07T13:32:39+00:00" + }, { "name": "doctrine/instantiator", "version": "1.0.5", From a2d175c4b5113da25eaeebcd9d58b19f5772dfc6 Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Mon, 1 May 2017 11:52:03 +0200 Subject: [PATCH 32/92] updated reference to ckeditor in tpl files --- modules/candidates/SendEmail.tpl | 2 +- modules/joborders/Add.tpl | 2 +- modules/joborders/Edit.tpl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/candidates/SendEmail.tpl b/modules/candidates/SendEmail.tpl index 8103e3765..ab72431fc 100755 --- a/modules/candidates/SendEmail.tpl +++ b/modules/candidates/SendEmail.tpl @@ -1,5 +1,5 @@ - + active); ?>
diff --git a/modules/joborders/Add.tpl b/modules/joborders/Add.tpl index 3ae1b1e2e..4eba9a657 100755 --- a/modules/joborders/Add.tpl +++ b/modules/joborders/Add.tpl @@ -1,5 +1,5 @@ - + active, $this->subActive); ?>
diff --git a/modules/joborders/Edit.tpl b/modules/joborders/Edit.tpl index a9ba72334..ec9f8d060 100755 --- a/modules/joborders/Edit.tpl +++ b/modules/joborders/Edit.tpl @@ -1,5 +1,5 @@ - + active); ?>
From 00f2d3439a0be6ba66955e2e26b7a3c296e56a81 Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Mon, 1 May 2017 11:56:17 +0200 Subject: [PATCH 33/92] removed embedded ckeditor files --- ckeditor/.htaccess | 24 - ckeditor/CHANGES.html | 538 ------ ckeditor/CHANGES.md | 1118 ----------- ckeditor/INSTALL.html | 92 - ckeditor/LICENSE.html | 1334 -------------- ckeditor/LICENSE.md | 1420 -------------- ckeditor/README.md | 39 - ckeditor/adapters/jquery.js | 10 - ckeditor/build-config.js | 159 -- ckeditor/ckeditor.js | 1017 ---------- ckeditor/ckeditor.pack | 205 --- ckeditor/ckeditor.php | 29 - ckeditor/ckeditor_basic.js | 8 - ckeditor/ckeditor_basic_source.js | 20 - ckeditor/ckeditor_php4.php | 593 ------ ckeditor/ckeditor_php5.php | 583 ------ ckeditor/ckeditor_source.js | 25 - ckeditor/config.js | 40 - ckeditor/contents.css | 135 -- ckeditor/lang/af.js | 5 - ckeditor/lang/ar.js | 5 - ckeditor/lang/bg.js | 5 - ckeditor/lang/bn.js | 5 - ckeditor/lang/bs.js | 5 - ckeditor/lang/ca.js | 5 - ckeditor/lang/cs.js | 5 - ckeditor/lang/cy.js | 5 - ckeditor/lang/da.js | 5 - ckeditor/lang/de-ch.js | 5 - ckeditor/lang/de.js | 5 - ckeditor/lang/el.js | 5 - ckeditor/lang/en-au.js | 5 - ckeditor/lang/en-ca.js | 5 - ckeditor/lang/en-gb.js | 5 - ckeditor/lang/en.js | 5 - ckeditor/lang/eo.js | 5 - ckeditor/lang/es.js | 5 - ckeditor/lang/et.js | 5 - ckeditor/lang/eu.js | 5 - ckeditor/lang/fa.js | 5 - ckeditor/lang/fi.js | 5 - ckeditor/lang/fo.js | 5 - ckeditor/lang/fr-ca.js | 5 - ckeditor/lang/fr.js | 5 - ckeditor/lang/gl.js | 5 - ckeditor/lang/gu.js | 5 - ckeditor/lang/he.js | 5 - ckeditor/lang/hi.js | 5 - ckeditor/lang/hr.js | 5 - ckeditor/lang/hu.js | 5 - ckeditor/lang/id.js | 5 - ckeditor/lang/is.js | 5 - ckeditor/lang/it.js | 5 - ckeditor/lang/ja.js | 5 - ckeditor/lang/ka.js | 5 - ckeditor/lang/km.js | 5 - ckeditor/lang/ko.js | 5 - ckeditor/lang/ku.js | 5 - ckeditor/lang/lt.js | 5 - ckeditor/lang/lv.js | 5 - ckeditor/lang/mk.js | 5 - ckeditor/lang/mn.js | 5 - ckeditor/lang/ms.js | 5 - ckeditor/lang/nb.js | 5 - ckeditor/lang/nl.js | 5 - ckeditor/lang/no.js | 5 - ckeditor/lang/pl.js | 5 - ckeditor/lang/pt-br.js | 5 - ckeditor/lang/pt.js | 5 - ckeditor/lang/ro.js | 5 - ckeditor/lang/ru.js | 5 - ckeditor/lang/si.js | 5 - ckeditor/lang/sk.js | 5 - ckeditor/lang/sl.js | 5 - ckeditor/lang/sq.js | 5 - ckeditor/lang/sr-latn.js | 5 - ckeditor/lang/sr.js | 5 - ckeditor/lang/sv.js | 5 - ckeditor/lang/th.js | 5 - ckeditor/lang/tr.js | 5 - ckeditor/lang/tt.js | 5 - ckeditor/lang/ug.js | 5 - ckeditor/lang/uk.js | 5 - ckeditor/lang/vi.js | 5 - ckeditor/lang/zh-cn.js | 5 - ckeditor/lang/zh.js | 5 - ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js | 10 - .../dialogs/lang/_translationstatus.txt | 25 - ckeditor/plugins/a11yhelp/dialogs/lang/af.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/ar.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/bg.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/ca.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/cs.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/cy.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/da.js | 11 - .../plugins/a11yhelp/dialogs/lang/de-ch.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/de.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/el.js | 12 - .../plugins/a11yhelp/dialogs/lang/en-gb.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/en.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/eo.js | 13 - ckeditor/plugins/a11yhelp/dialogs/lang/es.js | 13 - ckeditor/plugins/a11yhelp/dialogs/lang/et.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/eu.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/fa.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/fi.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/fo.js | 11 - .../plugins/a11yhelp/dialogs/lang/fr-ca.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/fr.js | 13 - ckeditor/plugins/a11yhelp/dialogs/lang/gl.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/gu.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/he.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/hi.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/hr.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/hu.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/id.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/it.js | 13 - ckeditor/plugins/a11yhelp/dialogs/lang/ja.js | 9 - ckeditor/plugins/a11yhelp/dialogs/lang/km.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/ko.js | 10 - ckeditor/plugins/a11yhelp/dialogs/lang/ku.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/lt.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/lv.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/mk.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/mn.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/nb.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/nl.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/no.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/pl.js | 13 - .../plugins/a11yhelp/dialogs/lang/pt-br.js | 13 - ckeditor/plugins/a11yhelp/dialogs/lang/pt.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/ro.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/ru.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/si.js | 10 - ckeditor/plugins/a11yhelp/dialogs/lang/sk.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/sl.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/sq.js | 11 - .../plugins/a11yhelp/dialogs/lang/sr-latn.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/sr.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/sv.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/th.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/tr.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/tt.js | 11 - ckeditor/plugins/a11yhelp/dialogs/lang/ug.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/uk.js | 12 - ckeditor/plugins/a11yhelp/dialogs/lang/vi.js | 11 - .../plugins/a11yhelp/dialogs/lang/zh-cn.js | 9 - ckeditor/plugins/a11yhelp/dialogs/lang/zh.js | 9 - ckeditor/plugins/about/dialogs/about.js | 8 - .../about/dialogs/hidpi/logo_ckeditor.png | Bin 13339 -> 0 bytes .../plugins/about/dialogs/logo_ckeditor.png | Bin 6757 -> 0 bytes ckeditor/plugins/clipboard/dialogs/paste.js | 12 - ckeditor/plugins/dialog/dialogDefinition.js | 4 - ckeditor/plugins/font/lang/af.js | 14 - ckeditor/plugins/font/lang/ar.js | 14 - ckeditor/plugins/font/lang/bg.js | 14 - ckeditor/plugins/font/lang/bn.js | 14 - ckeditor/plugins/font/lang/bs.js | 14 - ckeditor/plugins/font/lang/ca.js | 14 - ckeditor/plugins/font/lang/cs.js | 14 - ckeditor/plugins/font/lang/cy.js | 14 - ckeditor/plugins/font/lang/da.js | 14 - ckeditor/plugins/font/lang/de-ch.js | 14 - ckeditor/plugins/font/lang/de.js | 14 - ckeditor/plugins/font/lang/el.js | 14 - ckeditor/plugins/font/lang/en-au.js | 14 - ckeditor/plugins/font/lang/en-ca.js | 14 - ckeditor/plugins/font/lang/en-gb.js | 14 - ckeditor/plugins/font/lang/en.js | 14 - ckeditor/plugins/font/lang/eo.js | 14 - ckeditor/plugins/font/lang/es.js | 14 - ckeditor/plugins/font/lang/et.js | 14 - ckeditor/plugins/font/lang/eu.js | 14 - ckeditor/plugins/font/lang/fa.js | 14 - ckeditor/plugins/font/lang/fi.js | 14 - ckeditor/plugins/font/lang/fo.js | 14 - ckeditor/plugins/font/lang/fr-ca.js | 14 - ckeditor/plugins/font/lang/fr.js | 14 - ckeditor/plugins/font/lang/gl.js | 14 - ckeditor/plugins/font/lang/gu.js | 14 - ckeditor/plugins/font/lang/he.js | 14 - ckeditor/plugins/font/lang/hi.js | 14 - ckeditor/plugins/font/lang/hr.js | 14 - ckeditor/plugins/font/lang/hu.js | 14 - ckeditor/plugins/font/lang/id.js | 14 - ckeditor/plugins/font/lang/is.js | 14 - ckeditor/plugins/font/lang/it.js | 14 - ckeditor/plugins/font/lang/ja.js | 14 - ckeditor/plugins/font/lang/ka.js | 14 - ckeditor/plugins/font/lang/km.js | 14 - ckeditor/plugins/font/lang/ko.js | 14 - ckeditor/plugins/font/lang/ku.js | 14 - ckeditor/plugins/font/lang/lt.js | 14 - ckeditor/plugins/font/lang/lv.js | 14 - ckeditor/plugins/font/lang/mk.js | 14 - ckeditor/plugins/font/lang/mn.js | 14 - ckeditor/plugins/font/lang/ms.js | 14 - ckeditor/plugins/font/lang/nb.js | 14 - ckeditor/plugins/font/lang/nl.js | 14 - ckeditor/plugins/font/lang/no.js | 14 - ckeditor/plugins/font/lang/oc.js | 14 - ckeditor/plugins/font/lang/pl.js | 14 - ckeditor/plugins/font/lang/pt-br.js | 14 - ckeditor/plugins/font/lang/pt.js | 14 - ckeditor/plugins/font/lang/ro.js | 14 - ckeditor/plugins/font/lang/ru.js | 14 - ckeditor/plugins/font/lang/si.js | 14 - ckeditor/plugins/font/lang/sk.js | 14 - ckeditor/plugins/font/lang/sl.js | 14 - ckeditor/plugins/font/lang/sq.js | 14 - ckeditor/plugins/font/lang/sr-latn.js | 14 - ckeditor/plugins/font/lang/sr.js | 14 - ckeditor/plugins/font/lang/sv.js | 14 - ckeditor/plugins/font/lang/th.js | 14 - ckeditor/plugins/font/lang/tr.js | 14 - ckeditor/plugins/font/lang/tt.js | 14 - ckeditor/plugins/font/lang/ug.js | 14 - ckeditor/plugins/font/lang/uk.js | 14 - ckeditor/plugins/font/lang/vi.js | 14 - ckeditor/plugins/font/lang/zh-cn.js | 14 - ckeditor/plugins/font/lang/zh.js | 14 - ckeditor/plugins/font/plugin.js | 354 ---- ckeditor/plugins/icons.png | Bin 10227 -> 0 bytes ckeditor/plugins/icons_hidpi.png | Bin 34465 -> 0 bytes ckeditor/plugins/image/dialogs/image.js | 44 - ckeditor/plugins/image/images/noimage.png | Bin 1610 -> 0 bytes ckeditor/plugins/link/dialogs/anchor.js | 7 - ckeditor/plugins/link/dialogs/link.js | 27 - ckeditor/plugins/link/images/anchor.png | Bin 589 -> 0 bytes ckeditor/plugins/link/images/hidpi/anchor.png | Bin 1379 -> 0 bytes .../magicline/images/hidpi/icon-rtl.png | Bin 176 -> 0 bytes .../plugins/magicline/images/hidpi/icon.png | Bin 199 -> 0 bytes .../plugins/magicline/images/icon-rtl.png | Bin 138 -> 0 bytes ckeditor/plugins/magicline/images/icon.png | Bin 133 -> 0 bytes .../plugins/pastefromword/filter/default.js | 32 - ckeditor/plugins/scayt/CHANGELOG.md | 20 - ckeditor/plugins/scayt/LICENSE.md | 28 - ckeditor/plugins/scayt/README.md | 25 - ckeditor/plugins/scayt/dialogs/options.js | 19 - ckeditor/plugins/scayt/dialogs/toolbar.css | 71 - .../dialogs/lang/_translationstatus.txt | 20 - .../plugins/specialchar/dialogs/lang/af.js | 13 - .../plugins/specialchar/dialogs/lang/ar.js | 13 - .../plugins/specialchar/dialogs/lang/bg.js | 13 - .../plugins/specialchar/dialogs/lang/ca.js | 14 - .../plugins/specialchar/dialogs/lang/cs.js | 13 - .../plugins/specialchar/dialogs/lang/cy.js | 14 - .../plugins/specialchar/dialogs/lang/da.js | 11 - .../plugins/specialchar/dialogs/lang/de-ch.js | 13 - .../plugins/specialchar/dialogs/lang/de.js | 13 - .../plugins/specialchar/dialogs/lang/el.js | 13 - .../plugins/specialchar/dialogs/lang/en-gb.js | 13 - .../plugins/specialchar/dialogs/lang/en.js | 13 - .../plugins/specialchar/dialogs/lang/eo.js | 12 - .../plugins/specialchar/dialogs/lang/es.js | 13 - .../plugins/specialchar/dialogs/lang/et.js | 13 - .../plugins/specialchar/dialogs/lang/eu.js | 13 - .../plugins/specialchar/dialogs/lang/fa.js | 12 - .../plugins/specialchar/dialogs/lang/fi.js | 13 - .../plugins/specialchar/dialogs/lang/fr-ca.js | 10 - .../plugins/specialchar/dialogs/lang/fr.js | 12 - .../plugins/specialchar/dialogs/lang/gl.js | 13 - .../plugins/specialchar/dialogs/lang/he.js | 12 - .../plugins/specialchar/dialogs/lang/hr.js | 13 - .../plugins/specialchar/dialogs/lang/hu.js | 12 - .../plugins/specialchar/dialogs/lang/id.js | 13 - .../plugins/specialchar/dialogs/lang/it.js | 14 - .../plugins/specialchar/dialogs/lang/ja.js | 9 - .../plugins/specialchar/dialogs/lang/km.js | 13 - .../plugins/specialchar/dialogs/lang/ko.js | 10 - .../plugins/specialchar/dialogs/lang/ku.js | 13 - .../plugins/specialchar/dialogs/lang/lt.js | 13 - .../plugins/specialchar/dialogs/lang/lv.js | 13 - .../plugins/specialchar/dialogs/lang/nb.js | 11 - .../plugins/specialchar/dialogs/lang/nl.js | 13 - .../plugins/specialchar/dialogs/lang/no.js | 11 - .../plugins/specialchar/dialogs/lang/pl.js | 12 - .../plugins/specialchar/dialogs/lang/pt-br.js | 11 - .../plugins/specialchar/dialogs/lang/pt.js | 13 - .../plugins/specialchar/dialogs/lang/ru.js | 13 - .../plugins/specialchar/dialogs/lang/si.js | 13 - .../plugins/specialchar/dialogs/lang/sk.js | 13 - .../plugins/specialchar/dialogs/lang/sl.js | 12 - .../plugins/specialchar/dialogs/lang/sq.js | 13 - .../plugins/specialchar/dialogs/lang/sv.js | 11 - .../plugins/specialchar/dialogs/lang/th.js | 13 - .../plugins/specialchar/dialogs/lang/tr.js | 12 - .../plugins/specialchar/dialogs/lang/tt.js | 13 - .../plugins/specialchar/dialogs/lang/ug.js | 13 - .../plugins/specialchar/dialogs/lang/uk.js | 12 - .../plugins/specialchar/dialogs/lang/vi.js | 14 - .../plugins/specialchar/dialogs/lang/zh-cn.js | 9 - .../plugins/specialchar/dialogs/lang/zh.js | 9 - .../specialchar/dialogs/specialchar.js | 14 - ckeditor/plugins/table/dialogs/table.js | 21 - .../plugins/tabletools/dialogs/tableCell.js | 17 - ckeditor/plugins/wsc/LICENSE.md | 28 - ckeditor/plugins/wsc/README.md | 25 - ckeditor/plugins/wsc/dialogs/ciframe.html | 66 - ckeditor/plugins/wsc/dialogs/tmpFrameset.html | 52 - ckeditor/plugins/wsc/dialogs/wsc.css | 82 - ckeditor/plugins/wsc/dialogs/wsc.js | 92 - ckeditor/plugins/wsc/dialogs/wsc_ie.js | 11 - ckeditor/samples/css/samples.css | 1640 ----------------- ckeditor/samples/img/github-top.png | Bin 383 -> 0 bytes ckeditor/samples/img/header-bg.png | Bin 13086 -> 0 bytes ckeditor/samples/img/header-separator.png | Bin 123 -> 0 bytes ckeditor/samples/img/logo.png | Bin 5891 -> 0 bytes ckeditor/samples/img/navigation-tip.png | Bin 12029 -> 0 bytes ckeditor/samples/index.html | 128 -- ckeditor/samples/js/sample.js | 53 - ckeditor/samples/js/sf.js | 17 - ckeditor/samples/old/ajax.html | 85 - ckeditor/samples/old/api.html | 210 --- ckeditor/samples/old/appendto.html | 59 - .../samples/old/assets/inlineall/logo.png | Bin 4283 -> 0 bytes .../old/assets/outputxhtml/outputxhtml.css | 204 -- ckeditor/samples/old/assets/posteddata.php | 59 - ckeditor/samples/old/assets/sample.jpg | Bin 14449 -> 0 bytes .../old/assets/uilanguages/languages.js | 7 - ckeditor/samples/old/datafiltering.html | 508 ----- .../samples/old/dialog/assets/my_dialog.js | 48 - ckeditor/samples/old/dialog/dialog.html | 190 -- ckeditor/samples/old/divreplace.html | 144 -- ckeditor/samples/old/enterkey/enterkey.html | 106 -- .../assets/outputforflash/outputforflash.fla | Bin 85504 -> 0 bytes .../assets/outputforflash/outputforflash.swf | Bin 15571 -> 0 bytes .../assets/outputforflash/swfobject.js | 19 - .../old/htmlwriter/outputforflash.html | 283 --- .../samples/old/htmlwriter/outputhtml.html | 224 --- ckeditor/samples/old/index.html | 131 -- ckeditor/samples/old/inlineall.html | 314 ---- ckeditor/samples/old/inlinebycode.html | 124 -- ckeditor/samples/old/inlinetextarea.html | 113 -- ckeditor/samples/old/jquery.html | 103 -- ckeditor/samples/old/magicline/magicline.html | 209 --- ckeditor/samples/old/readonly.html | 76 - ckeditor/samples/old/replacebyclass.html | 60 - ckeditor/samples/old/replacebycode.html | 59 - ckeditor/samples/old/sample.css | 357 ---- ckeditor/samples/old/sample.js | 50 - ckeditor/samples/old/sample_posteddata.php | 16 - ckeditor/samples/old/tabindex.html | 78 - ckeditor/samples/old/toolbar/toolbar.html | 235 --- ckeditor/samples/old/uicolor.html | 72 - ckeditor/samples/old/uilanguages.html | 122 -- .../samples/old/wysiwygarea/fullpage.html | 80 - ckeditor/samples/old/xhtmlstyle.html | 234 --- .../toolbarconfigurator/css/fontello.css | 55 - .../toolbarconfigurator/font/LICENSE.txt | 10 - .../toolbarconfigurator/font/config.json | 28 - .../toolbarconfigurator/font/fontello.eot | Bin 4988 -> 0 bytes .../toolbarconfigurator/font/fontello.svg | 14 - .../toolbarconfigurator/font/fontello.ttf | Bin 4820 -> 0 bytes .../toolbarconfigurator/font/fontello.woff | Bin 2904 -> 0 bytes .../samples/toolbarconfigurator/index.html | 446 ----- .../js/abstracttoolbarmodifier.js | 13 - .../js/fulltoolbareditor.js | 9 - .../toolbarconfigurator/js/toolbarmodifier.js | 33 - .../js/toolbartextmodifier.js | 14 - .../lib/codemirror/LICENSE | 19 - .../lib/codemirror/codemirror.css | 325 ---- .../lib/codemirror/codemirror.js | 288 --- .../lib/codemirror/javascript.js | 25 - .../lib/codemirror/neo.css | 36 - .../lib/codemirror/show-hint.css | 38 - .../lib/codemirror/show-hint.js | 16 - ckeditor/skins/moono/dialog.css | 5 - ckeditor/skins/moono/dialog_ie.css | 5 - ckeditor/skins/moono/dialog_ie7.css | 5 - ckeditor/skins/moono/dialog_ie8.css | 5 - ckeditor/skins/moono/dialog_iequirks.css | 5 - ckeditor/skins/moono/editor.css | 5 - ckeditor/skins/moono/editor_gecko.css | 5 - ckeditor/skins/moono/editor_ie.css | 5 - ckeditor/skins/moono/editor_ie7.css | 5 - ckeditor/skins/moono/editor_ie8.css | 5 - ckeditor/skins/moono/editor_iequirks.css | 5 - ckeditor/skins/moono/icons.png | Bin 10227 -> 0 bytes ckeditor/skins/moono/icons_hidpi.png | Bin 34465 -> 0 bytes ckeditor/skins/moono/images/arrow.png | Bin 191 -> 0 bytes ckeditor/skins/moono/images/close.png | Bin 468 -> 0 bytes ckeditor/skins/moono/images/hidpi/close.png | Bin 1271 -> 0 bytes .../skins/moono/images/hidpi/lock-open.png | Bin 1329 -> 0 bytes ckeditor/skins/moono/images/hidpi/lock.png | Bin 1299 -> 0 bytes ckeditor/skins/moono/images/hidpi/refresh.png | Bin 1842 -> 0 bytes ckeditor/skins/moono/images/lock-open.png | Bin 349 -> 0 bytes ckeditor/skins/moono/images/lock.png | Bin 475 -> 0 bytes ckeditor/skins/moono/images/refresh.png | Bin 422 -> 0 bytes ckeditor/skins/moono/images/spinner.gif | Bin 2984 -> 0 bytes ckeditor/skins/moono/readme.md | 49 - ckeditor/styles.js | 111 -- 392 files changed, 19112 deletions(-) delete mode 100644 ckeditor/.htaccess delete mode 100644 ckeditor/CHANGES.html delete mode 100644 ckeditor/CHANGES.md delete mode 100644 ckeditor/INSTALL.html delete mode 100644 ckeditor/LICENSE.html delete mode 100644 ckeditor/LICENSE.md delete mode 100644 ckeditor/README.md delete mode 100644 ckeditor/adapters/jquery.js delete mode 100644 ckeditor/build-config.js delete mode 100644 ckeditor/ckeditor.js delete mode 100644 ckeditor/ckeditor.pack delete mode 100644 ckeditor/ckeditor.php delete mode 100644 ckeditor/ckeditor_basic.js delete mode 100644 ckeditor/ckeditor_basic_source.js delete mode 100644 ckeditor/ckeditor_php4.php delete mode 100644 ckeditor/ckeditor_php5.php delete mode 100644 ckeditor/ckeditor_source.js delete mode 100644 ckeditor/config.js delete mode 100644 ckeditor/contents.css delete mode 100644 ckeditor/lang/af.js delete mode 100644 ckeditor/lang/ar.js delete mode 100644 ckeditor/lang/bg.js delete mode 100644 ckeditor/lang/bn.js delete mode 100644 ckeditor/lang/bs.js delete mode 100644 ckeditor/lang/ca.js delete mode 100644 ckeditor/lang/cs.js delete mode 100644 ckeditor/lang/cy.js delete mode 100644 ckeditor/lang/da.js delete mode 100644 ckeditor/lang/de-ch.js delete mode 100644 ckeditor/lang/de.js delete mode 100644 ckeditor/lang/el.js delete mode 100644 ckeditor/lang/en-au.js delete mode 100644 ckeditor/lang/en-ca.js delete mode 100644 ckeditor/lang/en-gb.js delete mode 100644 ckeditor/lang/en.js delete mode 100644 ckeditor/lang/eo.js delete mode 100644 ckeditor/lang/es.js delete mode 100644 ckeditor/lang/et.js delete mode 100644 ckeditor/lang/eu.js delete mode 100644 ckeditor/lang/fa.js delete mode 100644 ckeditor/lang/fi.js delete mode 100644 ckeditor/lang/fo.js delete mode 100644 ckeditor/lang/fr-ca.js delete mode 100644 ckeditor/lang/fr.js delete mode 100644 ckeditor/lang/gl.js delete mode 100644 ckeditor/lang/gu.js delete mode 100644 ckeditor/lang/he.js delete mode 100644 ckeditor/lang/hi.js delete mode 100644 ckeditor/lang/hr.js delete mode 100644 ckeditor/lang/hu.js delete mode 100644 ckeditor/lang/id.js delete mode 100644 ckeditor/lang/is.js delete mode 100644 ckeditor/lang/it.js delete mode 100644 ckeditor/lang/ja.js delete mode 100644 ckeditor/lang/ka.js delete mode 100644 ckeditor/lang/km.js delete mode 100644 ckeditor/lang/ko.js delete mode 100644 ckeditor/lang/ku.js delete mode 100644 ckeditor/lang/lt.js delete mode 100644 ckeditor/lang/lv.js delete mode 100644 ckeditor/lang/mk.js delete mode 100644 ckeditor/lang/mn.js delete mode 100644 ckeditor/lang/ms.js delete mode 100644 ckeditor/lang/nb.js delete mode 100644 ckeditor/lang/nl.js delete mode 100644 ckeditor/lang/no.js delete mode 100644 ckeditor/lang/pl.js delete mode 100644 ckeditor/lang/pt-br.js delete mode 100644 ckeditor/lang/pt.js delete mode 100644 ckeditor/lang/ro.js delete mode 100644 ckeditor/lang/ru.js delete mode 100644 ckeditor/lang/si.js delete mode 100644 ckeditor/lang/sk.js delete mode 100644 ckeditor/lang/sl.js delete mode 100644 ckeditor/lang/sq.js delete mode 100644 ckeditor/lang/sr-latn.js delete mode 100644 ckeditor/lang/sr.js delete mode 100644 ckeditor/lang/sv.js delete mode 100644 ckeditor/lang/th.js delete mode 100644 ckeditor/lang/tr.js delete mode 100644 ckeditor/lang/tt.js delete mode 100644 ckeditor/lang/ug.js delete mode 100644 ckeditor/lang/uk.js delete mode 100644 ckeditor/lang/vi.js delete mode 100644 ckeditor/lang/zh-cn.js delete mode 100644 ckeditor/lang/zh.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/_translationstatus.txt delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/af.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ar.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/bg.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ca.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/cs.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/cy.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/da.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/de-ch.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/de.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/el.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/en-gb.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/en.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/eo.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/es.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/et.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/eu.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/fa.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/fi.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/fo.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/fr-ca.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/fr.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/gl.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/gu.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/he.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/hi.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/hr.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/hu.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/id.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/it.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ja.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/km.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ko.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ku.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/lt.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/lv.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/mk.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/mn.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/nb.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/nl.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/no.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/pl.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/pt-br.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/pt.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ro.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ru.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/si.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/sk.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/sl.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/sq.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/sr-latn.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/sr.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/sv.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/th.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/tr.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/tt.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/ug.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/uk.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/vi.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/zh-cn.js delete mode 100644 ckeditor/plugins/a11yhelp/dialogs/lang/zh.js delete mode 100644 ckeditor/plugins/about/dialogs/about.js delete mode 100644 ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png delete mode 100644 ckeditor/plugins/about/dialogs/logo_ckeditor.png delete mode 100644 ckeditor/plugins/clipboard/dialogs/paste.js delete mode 100644 ckeditor/plugins/dialog/dialogDefinition.js delete mode 100644 ckeditor/plugins/font/lang/af.js delete mode 100644 ckeditor/plugins/font/lang/ar.js delete mode 100644 ckeditor/plugins/font/lang/bg.js delete mode 100644 ckeditor/plugins/font/lang/bn.js delete mode 100644 ckeditor/plugins/font/lang/bs.js delete mode 100644 ckeditor/plugins/font/lang/ca.js delete mode 100644 ckeditor/plugins/font/lang/cs.js delete mode 100644 ckeditor/plugins/font/lang/cy.js delete mode 100644 ckeditor/plugins/font/lang/da.js delete mode 100644 ckeditor/plugins/font/lang/de-ch.js delete mode 100644 ckeditor/plugins/font/lang/de.js delete mode 100644 ckeditor/plugins/font/lang/el.js delete mode 100644 ckeditor/plugins/font/lang/en-au.js delete mode 100644 ckeditor/plugins/font/lang/en-ca.js delete mode 100644 ckeditor/plugins/font/lang/en-gb.js delete mode 100644 ckeditor/plugins/font/lang/en.js delete mode 100644 ckeditor/plugins/font/lang/eo.js delete mode 100644 ckeditor/plugins/font/lang/es.js delete mode 100644 ckeditor/plugins/font/lang/et.js delete mode 100644 ckeditor/plugins/font/lang/eu.js delete mode 100644 ckeditor/plugins/font/lang/fa.js delete mode 100644 ckeditor/plugins/font/lang/fi.js delete mode 100644 ckeditor/plugins/font/lang/fo.js delete mode 100644 ckeditor/plugins/font/lang/fr-ca.js delete mode 100644 ckeditor/plugins/font/lang/fr.js delete mode 100644 ckeditor/plugins/font/lang/gl.js delete mode 100644 ckeditor/plugins/font/lang/gu.js delete mode 100644 ckeditor/plugins/font/lang/he.js delete mode 100644 ckeditor/plugins/font/lang/hi.js delete mode 100644 ckeditor/plugins/font/lang/hr.js delete mode 100644 ckeditor/plugins/font/lang/hu.js delete mode 100644 ckeditor/plugins/font/lang/id.js delete mode 100644 ckeditor/plugins/font/lang/is.js delete mode 100644 ckeditor/plugins/font/lang/it.js delete mode 100644 ckeditor/plugins/font/lang/ja.js delete mode 100644 ckeditor/plugins/font/lang/ka.js delete mode 100644 ckeditor/plugins/font/lang/km.js delete mode 100644 ckeditor/plugins/font/lang/ko.js delete mode 100644 ckeditor/plugins/font/lang/ku.js delete mode 100644 ckeditor/plugins/font/lang/lt.js delete mode 100644 ckeditor/plugins/font/lang/lv.js delete mode 100644 ckeditor/plugins/font/lang/mk.js delete mode 100644 ckeditor/plugins/font/lang/mn.js delete mode 100644 ckeditor/plugins/font/lang/ms.js delete mode 100644 ckeditor/plugins/font/lang/nb.js delete mode 100644 ckeditor/plugins/font/lang/nl.js delete mode 100644 ckeditor/plugins/font/lang/no.js delete mode 100644 ckeditor/plugins/font/lang/oc.js delete mode 100644 ckeditor/plugins/font/lang/pl.js delete mode 100644 ckeditor/plugins/font/lang/pt-br.js delete mode 100644 ckeditor/plugins/font/lang/pt.js delete mode 100644 ckeditor/plugins/font/lang/ro.js delete mode 100644 ckeditor/plugins/font/lang/ru.js delete mode 100644 ckeditor/plugins/font/lang/si.js delete mode 100644 ckeditor/plugins/font/lang/sk.js delete mode 100644 ckeditor/plugins/font/lang/sl.js delete mode 100644 ckeditor/plugins/font/lang/sq.js delete mode 100644 ckeditor/plugins/font/lang/sr-latn.js delete mode 100644 ckeditor/plugins/font/lang/sr.js delete mode 100644 ckeditor/plugins/font/lang/sv.js delete mode 100644 ckeditor/plugins/font/lang/th.js delete mode 100644 ckeditor/plugins/font/lang/tr.js delete mode 100644 ckeditor/plugins/font/lang/tt.js delete mode 100644 ckeditor/plugins/font/lang/ug.js delete mode 100644 ckeditor/plugins/font/lang/uk.js delete mode 100644 ckeditor/plugins/font/lang/vi.js delete mode 100644 ckeditor/plugins/font/lang/zh-cn.js delete mode 100644 ckeditor/plugins/font/lang/zh.js delete mode 100644 ckeditor/plugins/font/plugin.js delete mode 100644 ckeditor/plugins/icons.png delete mode 100644 ckeditor/plugins/icons_hidpi.png delete mode 100644 ckeditor/plugins/image/dialogs/image.js delete mode 100644 ckeditor/plugins/image/images/noimage.png delete mode 100644 ckeditor/plugins/link/dialogs/anchor.js delete mode 100644 ckeditor/plugins/link/dialogs/link.js delete mode 100644 ckeditor/plugins/link/images/anchor.png delete mode 100644 ckeditor/plugins/link/images/hidpi/anchor.png delete mode 100644 ckeditor/plugins/magicline/images/hidpi/icon-rtl.png delete mode 100644 ckeditor/plugins/magicline/images/hidpi/icon.png delete mode 100644 ckeditor/plugins/magicline/images/icon-rtl.png delete mode 100644 ckeditor/plugins/magicline/images/icon.png delete mode 100644 ckeditor/plugins/pastefromword/filter/default.js delete mode 100644 ckeditor/plugins/scayt/CHANGELOG.md delete mode 100644 ckeditor/plugins/scayt/LICENSE.md delete mode 100644 ckeditor/plugins/scayt/README.md delete mode 100644 ckeditor/plugins/scayt/dialogs/options.js delete mode 100644 ckeditor/plugins/scayt/dialogs/toolbar.css delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/_translationstatus.txt delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/af.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ar.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/bg.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ca.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/cs.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/cy.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/da.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/de-ch.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/de.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/el.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/en-gb.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/en.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/eo.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/es.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/et.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/eu.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/fa.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/fi.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/fr-ca.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/fr.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/gl.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/he.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/hr.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/hu.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/id.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/it.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ja.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/km.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ko.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ku.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/lt.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/lv.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/nb.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/nl.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/no.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/pl.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/pt-br.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/pt.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ru.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/si.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/sk.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/sl.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/sq.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/sv.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/th.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/tr.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/tt.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/ug.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/uk.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/vi.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/zh-cn.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/lang/zh.js delete mode 100644 ckeditor/plugins/specialchar/dialogs/specialchar.js delete mode 100644 ckeditor/plugins/table/dialogs/table.js delete mode 100644 ckeditor/plugins/tabletools/dialogs/tableCell.js delete mode 100644 ckeditor/plugins/wsc/LICENSE.md delete mode 100644 ckeditor/plugins/wsc/README.md delete mode 100644 ckeditor/plugins/wsc/dialogs/ciframe.html delete mode 100644 ckeditor/plugins/wsc/dialogs/tmpFrameset.html delete mode 100644 ckeditor/plugins/wsc/dialogs/wsc.css delete mode 100644 ckeditor/plugins/wsc/dialogs/wsc.js delete mode 100644 ckeditor/plugins/wsc/dialogs/wsc_ie.js delete mode 100644 ckeditor/samples/css/samples.css delete mode 100644 ckeditor/samples/img/github-top.png delete mode 100644 ckeditor/samples/img/header-bg.png delete mode 100644 ckeditor/samples/img/header-separator.png delete mode 100644 ckeditor/samples/img/logo.png delete mode 100644 ckeditor/samples/img/navigation-tip.png delete mode 100644 ckeditor/samples/index.html delete mode 100644 ckeditor/samples/js/sample.js delete mode 100644 ckeditor/samples/js/sf.js delete mode 100644 ckeditor/samples/old/ajax.html delete mode 100644 ckeditor/samples/old/api.html delete mode 100644 ckeditor/samples/old/appendto.html delete mode 100644 ckeditor/samples/old/assets/inlineall/logo.png delete mode 100644 ckeditor/samples/old/assets/outputxhtml/outputxhtml.css delete mode 100644 ckeditor/samples/old/assets/posteddata.php delete mode 100644 ckeditor/samples/old/assets/sample.jpg delete mode 100644 ckeditor/samples/old/assets/uilanguages/languages.js delete mode 100644 ckeditor/samples/old/datafiltering.html delete mode 100644 ckeditor/samples/old/dialog/assets/my_dialog.js delete mode 100644 ckeditor/samples/old/dialog/dialog.html delete mode 100644 ckeditor/samples/old/divreplace.html delete mode 100644 ckeditor/samples/old/enterkey/enterkey.html delete mode 100644 ckeditor/samples/old/htmlwriter/assets/outputforflash/outputforflash.fla delete mode 100644 ckeditor/samples/old/htmlwriter/assets/outputforflash/outputforflash.swf delete mode 100644 ckeditor/samples/old/htmlwriter/assets/outputforflash/swfobject.js delete mode 100644 ckeditor/samples/old/htmlwriter/outputforflash.html delete mode 100644 ckeditor/samples/old/htmlwriter/outputhtml.html delete mode 100644 ckeditor/samples/old/index.html delete mode 100644 ckeditor/samples/old/inlineall.html delete mode 100644 ckeditor/samples/old/inlinebycode.html delete mode 100644 ckeditor/samples/old/inlinetextarea.html delete mode 100644 ckeditor/samples/old/jquery.html delete mode 100644 ckeditor/samples/old/magicline/magicline.html delete mode 100644 ckeditor/samples/old/readonly.html delete mode 100644 ckeditor/samples/old/replacebyclass.html delete mode 100644 ckeditor/samples/old/replacebycode.html delete mode 100644 ckeditor/samples/old/sample.css delete mode 100644 ckeditor/samples/old/sample.js delete mode 100644 ckeditor/samples/old/sample_posteddata.php delete mode 100644 ckeditor/samples/old/tabindex.html delete mode 100644 ckeditor/samples/old/toolbar/toolbar.html delete mode 100644 ckeditor/samples/old/uicolor.html delete mode 100644 ckeditor/samples/old/uilanguages.html delete mode 100644 ckeditor/samples/old/wysiwygarea/fullpage.html delete mode 100644 ckeditor/samples/old/xhtmlstyle.html delete mode 100644 ckeditor/samples/toolbarconfigurator/css/fontello.css delete mode 100644 ckeditor/samples/toolbarconfigurator/font/LICENSE.txt delete mode 100644 ckeditor/samples/toolbarconfigurator/font/config.json delete mode 100644 ckeditor/samples/toolbarconfigurator/font/fontello.eot delete mode 100644 ckeditor/samples/toolbarconfigurator/font/fontello.svg delete mode 100644 ckeditor/samples/toolbarconfigurator/font/fontello.ttf delete mode 100644 ckeditor/samples/toolbarconfigurator/font/fontello.woff delete mode 100644 ckeditor/samples/toolbarconfigurator/index.html delete mode 100644 ckeditor/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js delete mode 100644 ckeditor/samples/toolbarconfigurator/js/fulltoolbareditor.js delete mode 100644 ckeditor/samples/toolbarconfigurator/js/toolbarmodifier.js delete mode 100644 ckeditor/samples/toolbarconfigurator/js/toolbartextmodifier.js delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/LICENSE delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/codemirror.css delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/codemirror.js delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/javascript.js delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/neo.css delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/show-hint.css delete mode 100644 ckeditor/samples/toolbarconfigurator/lib/codemirror/show-hint.js delete mode 100644 ckeditor/skins/moono/dialog.css delete mode 100644 ckeditor/skins/moono/dialog_ie.css delete mode 100644 ckeditor/skins/moono/dialog_ie7.css delete mode 100644 ckeditor/skins/moono/dialog_ie8.css delete mode 100644 ckeditor/skins/moono/dialog_iequirks.css delete mode 100644 ckeditor/skins/moono/editor.css delete mode 100644 ckeditor/skins/moono/editor_gecko.css delete mode 100644 ckeditor/skins/moono/editor_ie.css delete mode 100644 ckeditor/skins/moono/editor_ie7.css delete mode 100644 ckeditor/skins/moono/editor_ie8.css delete mode 100644 ckeditor/skins/moono/editor_iequirks.css delete mode 100644 ckeditor/skins/moono/icons.png delete mode 100644 ckeditor/skins/moono/icons_hidpi.png delete mode 100644 ckeditor/skins/moono/images/arrow.png delete mode 100644 ckeditor/skins/moono/images/close.png delete mode 100644 ckeditor/skins/moono/images/hidpi/close.png delete mode 100644 ckeditor/skins/moono/images/hidpi/lock-open.png delete mode 100644 ckeditor/skins/moono/images/hidpi/lock.png delete mode 100644 ckeditor/skins/moono/images/hidpi/refresh.png delete mode 100644 ckeditor/skins/moono/images/lock-open.png delete mode 100644 ckeditor/skins/moono/images/lock.png delete mode 100644 ckeditor/skins/moono/images/refresh.png delete mode 100644 ckeditor/skins/moono/images/spinner.gif delete mode 100644 ckeditor/skins/moono/readme.md delete mode 100644 ckeditor/styles.js diff --git a/ckeditor/.htaccess b/ckeditor/.htaccess deleted file mode 100644 index ed1d794fe..000000000 --- a/ckeditor/.htaccess +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -# For licensing, see LICENSE.html or http://ckeditor.com/license -# - -# -# On some specific Linux installations you could face problems with Firefox. -# It could give you errors when loading the editor saying that some illegal -# characters were found (three strange chars in the beginning of the file). -# This could happen if you map the .js or .css files to PHP, for example. -# -# Those characters are the Byte Order Mask (BOM) of the Unicode encoded files. -# All FCKeditor files are Unicode encoded. -# - -AddType application/x-javascript .js -AddType text/css .css - -# -# If PHP is mapped to handle XML files, you could have some issues. The -# following will disable it. -# - -AddType text/xml .xml diff --git a/ckeditor/CHANGES.html b/ckeditor/CHANGES.html deleted file mode 100644 index 9b763b035..000000000 --- a/ckeditor/CHANGES.html +++ /dev/null @@ -1,538 +0,0 @@ - - - - - Changelog - CKEditor - - - - -

- CKEditor Changelog -

-

- CKEditor 3.2

-

- New features:

-
    -
  • Several accessibility enhancements:
      -
    • #4502 : The editor accessibility is now totally based on WAI-ARIA.
    • -
    • #5015 : Adding accessibility help dialog plugin.
    • -
    • #5014 : Keyboard navigation compliance with screen reader suggested keys.
    • -
    • #4595 : Better accessibility in the Templates dialog.
    • -
    • #3389 : Esc/Arrow Key now works for closing sub menu.
    • -
  • -
  • #4973 : The Style field in the Div Container dialog is now loading the styles defined in the default styleset used by the Styles toolbar combo.
  • -
-

- Fixed issues:

-
    -
  • #5049 : Form Field list command in JAWS incorrectly lists extra fields.
  • -
  • #5008 : Lock/Unlock ratio buttons in the Image dialog was poorly designed in High Contrast mode.
  • -
  • #3980 : All labels in dialogs now use <label> instead of <div>.
  • -
  • #5213 : Reorganization of some entries in the language files to make it more consistent.
  • -
  • #5199 : In IE, single row toolbars didn't have the bottom padding.
  • -
-

- CKEditor 3.1.1

-

- New features:

-
    -
  • #4399 : Improved support for external file browsers by allowing executing a callback function.
  • -
  • #4612 : The text of links is now updated if it matches the URL to which it points to.
  • -
  • #4936 : New localization support for the Welsh language.
  • -
-

- Fixed issues:

-
    -
  • #4272 : Kama skin toolbar was broken in IE+Quirks+RTL.
  • -
  • #4987 : Changed the url which is called by the Browser Server button in the Link tab of Image Properties dialog.
  • -
  • #5030 : The CKEDITOR.timestamp wasn't been appended to the skin.js file.
  • -
  • #4993 : Removed the float style from images when the user selects 'not set' for alignment.
  • -
  • #4944 : Fixed a bug where nested list structures with inconsequent levels were not being pasted correctly from MS Word.
  • -
  • #4637 : Table cells' 'nowrap' attribute was not being loaded by the cell property dialog. Thanks to pomu0325.
  • -
  • #4724 : Using the mouse to insert a link in IE might create incorrect results.
  • -
  • #4640 : Small optimizations for the fileBrowser plugin.
  • -
  • #4583 : The "Target Frame Name" field is now visible when target is set to 'frame' only.
  • -
  • #4863 : Fixing iframedialog's height doesn't stretch to 100% (except IE Quirks).
  • -
  • #4964 : The BACKSPACE key positioning was not correct in some cases with Firefox.
  • -
  • #4980 : Setting border, vspace and hspace of images to zero was not working.
  • -
  • #4773 : The fileBrowser plugin was overwriting onClick functions eventually defined on fileButton elements.
  • -
  • #4731 : The clipboard plugin was missing a reference to the dialog plugin.
  • -
  • #5051 : The about plugin was missing a reference to the dialog plugin.
  • -
  • #5146 : The wsc plugin was missing a reference to the dialog plugin.
  • -
  • #4632 : The print command will now properly break on the insertion point of page break for printing.
  • -
  • #4862 : The English (United Kingdom) language file has been renamed to en-gb.js.
  • -
  • #4618 : Selecting an emoticon or the lock and reset buttons in the image dialog fired the onBeforeUnload event in IE.
  • -
  • #4678 : It was not possible to set tables' width to empty value.
  • -
  • #5012 : Fixed dependency issues with the menu plugin.
  • -
  • #5040 : The editor will not properly ignore font related settings that have extra item separators (semi-colons).
  • -
  • #4046 : Justify should respect config.enterMode = CKEDITOR.ENTER_BR.
  • -
  • #4622 : Inserting tables multiple times was corrupting the undo system.
  • -
  • #4647 : [IE] Selection on an element within positioned container is lost after open context-menu then click one menu item.
  • -
  • #4683 : Double-quote character in attribute values was not escaped in the editor output.
  • -
  • #4762 : [IE] Unexpected vertical-scrolling behavior happens whenever focus is moving out of editor in source mode.
  • -
  • #4772 : Text color was not being applied properly on links.
  • -
  • #4795 : [IE] Press 'Del' key on horizontal line or table result in error.
  • -
  • #4824 : [IE] <br/> at the very first table cell breaks the editor selection.
  • -
  • #4851 : [IE] Delete table rows with context-menu may cause error.
  • -
  • #4951 : Replacing text with empty string was throwing errors.
  • -
  • #4963 : Link dialog was not opening properly for e-mail type links.
  • -
  • #5043 : Removed the possibility of having an unwanted script tag being outputted with the editor contents.
  • -
  • #3678 : There were issues when editing links inside floating divs with IE.
  • -
  • #4763 : Pressing ENTER key with text selected was not deleting the text in some situations.
  • -
  • #5096 : Simple ampersand attribute value doesn't work for more than one occurrence.
  • -
  • #3494 : Context menu is too narrow in some translations.
  • -
  • #5005 : Fixed HTML errors in PHP samples.
  • -
  • #5123 : Fixed broken XHTML in User Interface Languages sample.
  • -
  • #4893 : Editor now understands table cell inline styles.
  • -
  • #4611 : Selection around <select> in editor doesn't cause error anymore.
  • -
  • #4886 : Extra BR tags were being created in the output HTML.
  • -
  • #4933 : Empty tags with BR were being left in the DOM.
  • -
  • #5127 : There were errors when removing dialog definition pages through code.
  • -
  • #4767 : CKEditor was not working when ckeditor_source.js is loaded in the <body> .
  • -
  • #5062 : Avoided security warning message when loading the wysiwyg area in IE6 under HTTPS.
  • -
  • #5135 : The TAB key will now behave properly when in Source mode.
  • -
  • #4988 : It wasn't possible to use forcePasteAsPlainText with Safari on Mac.
  • -
  • #5095 : Safari on Mac deleted the current selection in the editor when Edit menu was clicked.
  • -
  • #5140 : In High Contrast mode, arrows were now been displayed for menus with submenus.
  • -
  • #5163 : The undo system was not working on some specific cases.
  • -
  • #5162 : The ajax sample was throwing errors when loading data.
  • -
  • #4999 : The Template dialog was not generating an undo snapshot.
  • -
  • Updated the following language files:
  • -
-

- CKEditor 3.1

-

- New features:

-
    -
  • #4067 : Introduced the full page editing support (from <html> to </html>).
  • -
  • #4228 : Introduced the Shared Spaces feature.
  • -
  • #4379 : Introduced the new powerful pasting system and word cleanup procedure, including enhancements to the paste as plain text feature.
  • -
  • #2872 : Introduced the new native PHP API, the first standardized server side support.
  • -
  • #4210 : Added CKEditor plugin for jQuery.
  • -
  • #2885 : Added 'div' dialog and corresponding context menu options.
  • -
  • #4574 : Added the table merging tools and corresponding context menu options.
  • -
  • #4340 : Added the email protection option for link dialog.
  • -
  • #4463 : Added inline CSS support in all places where custom stylesheet could apply.
  • -
  • #3881 : Added color dialog for 'more color' option in color buttons.
  • -
  • #4341 : Added the 'showborder' plugin.
  • -
  • #4549 : Make the anti-cache query string configurable.
  • -
  • #4708 : Added the 'htmlEncodeOutput' config option.
  • -
  • #4342 : Introduced the bodyId and bodyClass settings to specify the id and class. to be used in the editing area at runtime.
  • -
  • #3401 : Introduced the baseHref setting so it's possible to set the URL to be used to resolve absolute and relative URLs in the contents.
  • -
  • #4729 : Added support to fake elements for comments.
  • -
-

- Fixed issues:

-
    -
  • #4707 : Fixed invalid link is requested in image preview.
  • -
  • #4461 : Fixed toolbar separator line along side combo enlarging the toolbar height.
  • -
  • #4596 : Fixed image re-size lock buttons aren't accessible in high-contrast mode.
  • -
  • #4676 : Fixed editing tables using table properties dialog overwrites original style values.
  • -
  • #4714 : Fixed IE6 JavaScript error when editing flash by commit 'Flash' dialog.
  • -
  • #3905 : Fixed 'wysiwyg' mode causes unauthenticated content warnings over SSL in FF 3.5.
  • -
  • #4768 : Fixed open context menu in IE throws js error when focus is not inside document.
  • -
  • #4822 : Fixed applying 'Headers' to existing table does not work in IE.
  • -
  • #4855 : Fixed toolbar doesn't wrap well for 'v2' skin in all browsers.
  • -
  • #4882 : Fixed auto detect paste from MS-Word is not working for Safari.
  • -
  • #4882 : Fixed unexpected margin style left behind on content cleaning up from MS-Word.
  • -
  • #4896 : Fixed paste nested list from MS-Word with measurement units set to cm is broken.
  • -
  • #4899 : Fixed unable to undo pre-formatted style.
  • -
  • #4900 : Fixed ratio-lock inconsistent between browsers.
  • -
  • #4901 : Fixed unable to edit any link with popup window's features in Firefox.
  • -
  • #4904 : Fixed when paste happen from dialog, it always throw JavaScript error.
  • -
  • #4905 : Fixed paste plain text result incorrect when content from dialog.
  • -
  • #4889 : Fixed unable to undo 'New Page' command after typing inside editor.
  • -
  • #4892 : Fixed table alignment style is not properly represented by the wrapping div.
  • -
  • #4918 : Fixed switching mode when maximized is showing background page contents.
  • -
-

- CKEditor 3.0.2

-

- New features:

-
    -
  • #4343 : Added the configuration option 'browserContextMenuOnCtrl' so it's possible to enable the default browser context menu by holding the CTRL key.
  • -
-

- Fixed issues:

-
    -
  • #4552 : Fixed float panel doesn't show up since editor instanced been destroyed once.
  • -
  • #3918 : Fixed fake object is editable with Image dialog.
  • -
  • #4053 : Fixed 'Form Properties' missing from context menu when selection collapsed inside form.
  • -
  • #4401 : Fixed customized by removing 'upload' tab page from 'Link dialog' cause JavaScript error.
  • -
  • #4477 : Adding missing tag names in object style elements.
  • -
  • #4567 : Fixed IE throw error when pressing BACKSPACE in source mode.
  • -
  • #4573 : Fixed 'IgnoreEmptyPargraph' config doesn't work with the config 'entities' is set to 'false'.
  • -
  • #4614 : Fixed attribute protection fails because of line-break.
  • -
  • #4546 : Fixed UIColor plugin doesn't work when editor id contains CSS selector preserved keywords.
  • -
  • #4609 : Fixed flash object is lost when loading data from outside editor.
  • -
  • #4625 : Fixed editor stays visible in a div with style 'visibility:hidden'.
  • -
  • #4621 : Fixed clicking below table caused an empty table been generated.
  • -
  • #3373 : Fixed empty context menu when there's no menu item at all.
  • -
  • #4473 : Fixed setting rules on the same element tag name throws error.
  • -
  • #4514 : Fixed press 'Back' button breaks wysiwyg editing mode is Firefox.
  • -
  • #4542 : Fixed unable to access buttons using tab key in Safari and Opera.
  • -
  • #4577 : Fixed relative link url is broken after opening 'Link' dialog.
  • -
  • #4597 : Fixed custom style with same attribute name but different attribute value doesn't work.
  • -
  • #4651 : Fixed 'Deleted' and 'Inserted' text style is not rendering in wysiwyg mode and is wrong is source mode.
  • -
  • #4654 : Fixed 'CKEDITOR.config.font_defaultLabel(fontSize_defaultLabel)' is not working.
  • -
  • #3950 : Fixed table column insertion incorrect when selecting empty cell area.
  • -
  • #3912 : Fixed UIColor not working in IE when page has more than 30+ editors.
  • -
  • #4031 : Fixed mouse cursor on toolbar combo has more than 3 shapes.
  • -
  • #4041 : Fixed open context menu on multiple cells to remove them result in only one removed.
  • -
  • #4185 : Fixed resize handler effect doesn't affect flash object on output.
  • -
  • #4196 : Fixed 'Remove Numbered/Bulleted List' on nested list doesn't work well on nested list.
  • -
  • #4200 : Fixed unable to insert 'password' type filed with attributes.
  • -
  • #4530 : Fixed context menu couldn't open in Opera.
  • -
  • #4536 : Fixed keyboard navigation doesn't work at all in IE quirks mode.
  • -
  • #4584 : Fixed updated link Target field is not updating when updating to certain values.
  • -
  • #4603 : Fixed unable to disable submenu items in contextmenu.
  • -
  • #4672 : Fixed unable to redo the insertion of horizontal line.
  • -
  • #4677 : Fixed 'Tab' key is trapped by hidden dialog elements.
  • -
  • #4073 : Fixed insert template with replace option could result in empty document.
  • -
  • #4455 : Fixed unable to start editing when image inside document not loaded.
  • -
  • #4517 : Fixed 'dialog_backgroundCoverColor' doesn't work on IE6.
  • -
  • #3165 : Fixed enter key in empty list item before nested one result in collapsed line.
  • -
  • #4527 : Fixed checkbox generate invalid 'checked' attribute.
  • -
  • #1659 : Fixed unable to click below content to start editing in IE with 'config.docType' setting to standard compliant.
  • -
  • #3933 : Fixed extra <br> left at the end of document when the last element is a table.
  • -
  • #4736 : Fixed PAGE UP and PAGE DOWN keys in standards mode are not working.
  • -
  • #4725 : Fixed hitting 'enter' before html comment node produces a JavaScript error.
  • -
  • #4522 : Fixed unable to redo when typing after insert an image with relative url.
  • -
  • #4594 : Fixed context menu goes off-screen when mouse is at right had side of screen.
  • -
  • #4673 : Fixed undo not available straight away if shift key is used to enter first character.
  • -
  • #4690 : Fixed the parsing of nested inline elements.
  • -
  • #4450 : Fixed selecting multiple table cells before apply justify commands generates spurious paragraph in Firefox.
  • -
  • #4733 : Fixed dialog opening sometimes hang up Firefox and Safari.
  • -
  • #4498 : Fixed toolbar collapse button missing tooltip.
  • -
  • #4738 : Fixed inserting table inside bold/italic/underline generates error on ENTER_BR mode.
  • -
  • #4246 : Fixed avoid XHTML deprecated attributes for image styling.
  • -
  • #4543 : Fixed unable to move cursor between table and hr.
  • -
  • #4764 : Fixed wrong exception message when CKEDITOR.editor.append() to non-existing elements.
  • -
  • #4521 : Fixed dialog layout in IE6/7 may have scroll-bar and other weird effects.
  • -
  • #4709 : Fixed inconsistent scroll-bar behavior on IE.
  • -
  • #4776 : Fixed preview page failed to open when relative URl contains in document.
  • -
  • #4812 : Fixed 'Esc' key not working on dialogs in Opera.
  • -
  • Updated the following language files:
  • -
-

- CKEditor 3.0.1

-

- New features:

-
    -
  • #4219 : Added fallback mechanism for config.language.
  • -
  • #4194 : Added support for using multiple css style sheets within the editor.
  • -
-

- Fixed issues:

-
    -
  • #3898 : Added validation for URL value in Image dialog.
  • -
  • #3528 : Fixed Context Menu issue when triggered using Shift+F10.
  • -
  • #4028 : Maximize control's tool tip was wrong once it is maximized.
  • -
  • #4237 : Toolbar is chopped off in Safari browser 3.x.
  • -
  • #4241 : Float panels are left on screen while editor is destroyed.
  • -
  • #4274 : Double click event is incorrect handled in 'divreplace' sample.
  • -
  • #4354 : Fixed TAB key on toolbar to not focus disabled buttons.
  • -
  • #3856 : Fixed focus and blur events in source view mode.
  • -
  • #3438 : Floating panels are off by (-1px, 0px) in RTL mode.
  • -
  • #3370 : Refactored use of CKEDITOR.env.isCustomDomain().
  • -
  • #4230 : HC detection caused js error.
  • -
  • #3978 : Fixed setStyle float on IE7 strict.
  • -
  • #4262 : Tab and Shift+Tab was not working to cycle through CTRL+SHIFT+F10 context menu in IE.
  • -
  • #3633 : Default context menu isn't disabled in toolbar, status bar, panels...
  • -
  • #3897 : Now there is no image previews when the URL is empty in image dialog.
  • -
  • #4048 : Context submenu was lacking uiColor.
  • -
  • #3568 : Dialogs now select all text when tabbing to text inputs.
  • -
  • #3727 : Cell Properties dialog was missing color selection option.
  • -
  • #3517 : Fixed "Match cyclic" field in Find & Replace dialog.
  • -
  • #4368 : borderColor table cell attribute haven't worked for none-IE
  • -
  • #4203 : In IE quirks mode + toolbar collapsed + source mode editing block height was incorrect.
  • -
  • #4387 : Fixed: right clicking in Kama skin can lead to a javascript error.
  • -
  • #4397 : Wysiwyg mode caused the host page scroll.
  • -
  • #4385 : Fixed editor's auto adjusting on DOM structure were confusing the dirty checking mechanism.
  • -
  • #4397 : Fixed regression of [3816] where turn on design mode was causing Firefox3 to scroll the host page.
  • -
  • #4254 : Added basic API sample.
  • -
  • #4107 : Normalize css font-family style text for correct comparision.
  • -
  • #3664 : Insert block element in empty editor document should not create new paragraph.
  • -
  • #4037 : 'id' attribute is missing with Flash dialog advanced page.
  • -
  • #4047 : Delete selected control type element when 'Backspace' is pressed on it.
  • -
  • #4191 : Fixed: dialog changes confirmation on image dialog appeared even when no changes have been made.
  • -
  • #4351 : Dash and dot could appear in attribute names.
  • -
  • #4355 : 'maximize' and 'showblock' commands shouldn't take editor focus.
  • -
  • #4504 : Fixed 'Enter'/'Esc' key is not working on dialog button.
  • -
  • #4245 : 'Strange Template' now come with a style attribute for width.
  • -
  • #4512 : Fixed styles plugin incorrectly adding semicolons to style text.
  • -
  • #3855 : Fixed loading unminified _source files when ckeditor_source.js is used.
  • -
  • #3717 : Dialog settings defaults can now be overridden in-page through the CKEDITOR.config object.
  • -
  • #4481 : The 'stylesCombo_stylesSet' configuration entry didn't work for full URLs.
  • -
  • #4480 : Fixed scope attribute in th.
  • -
  • #4467 : Fixed bug to use custom icon in context menus. Thanks to george.
  • -
  • #4190 : Fixed select field dialog layout in Safari.
  • -
  • #4518 : Fixed unable to open dialog without editor focus in IE.
  • -
  • #4519 : Fixed maximize without editor focus throw error in IE.
  • -
  • Updated the following language files:
  • -
-

- CKEditor 3.0

-

- New features:

-
    -
  • #3188 : Introduce - <pre> formatting feature when converting from other blocks.
  • -
  • #4445 : editor::setData now support an optional callback parameter.
  • -
-

- Fixed issues:

-
    -
  • #2856 : Fixed problem with inches in Paste From Word plugin.
  • -
  • #3929 : Using Paste dialog, - the text is pasted into current selection
  • -
  • #3920 : Mouse cursor over characters in - Special Character dialog now is correct
  • -
  • #3882 : Fixed an issue - with PasteFromWord dialog in which default values was ignored
  • -
  • #3859 : Fixed Flash dialog layout in Webkit
  • -
  • #3852 : Disabled textarea resizing in dialogs
  • -
  • #3831 : The attempt to remove the contextmenu plugin - will not anymore break the editor
  • -
  • #3781 : Colorbutton is now disabled in 'source' mode
  • -
  • #3848 : Fixed an issue with Webkit in witch - elements in the Image and Link dialogs had wrong dimensions.
  • -
  • #3808 : Fixed UI Color Picker dialog size in example page.
  • -
  • #3658 : Editor had horizontal scrollbar in IE6.
  • -
  • #3819 : The cursor was not visible - when applying style to collapsed selections in Firefox 2.
  • -
  • #3809 : Fixed beam cursor - when mouse cursor is over text-only buttons in IE.
  • -
  • #3815 : Fixed an issue - with the form dialog in which the "enctype" attribute is outputted as "encoding".
  • -
  • #3785 : Fixed an issue - in CKEDITOR.tools.htmlEncode() which incorrectly outputs &nbsp; in IE8.
  • -
  • #3820 : Fixed an issue in - bullet list command in which a list created at the bottom of another gets merged to the top. -
  • -
  • #3830 : Table cell properties dialog - doesn't apply to all selected cells.
  • -
  • #3835 : Element path is not refreshed - after click on 'newpage'; and safari is not putting focus on document also. -
  • -
  • #3821 : Fixed an issue with JAWS in which - toolbar items are read inconsistently between virtual cursor modes.
  • -
  • #3789 : The "src" attribute - was getting duplicated in some situations.
  • -
  • #3591 : Protecting flash related elements - including '<object>', '<embed>' and '<param>'. -
  • -
  • #3759 : Fixed CKEDITOR.dom.element::scrollIntoView - logic bug which scroll even element is inside viewport. -
  • -
  • #3773 : Fixed remove list will merge lines. -
  • -
  • #3829 : Fixed remove empty link on output data.
  • -
  • #3730 : Indent is performing on the whole - block instead of selected lines in enterMode = BR.
  • -
  • #3844 : Fixed UndoManager register keydown on obsoleted document
  • -
  • #3805 : Enabled SCAYT plugin for IE.
  • -
  • #3834 : Context menu on table caption was incorrect.
  • -
  • #3812 : Fixed an issue in which the editor - may show up empty or uneditable in IE7, 8 and Firefox 3.
  • -
  • #3825 : Fixed JS error when opening spellingcheck.
  • -
  • #3862 : Fixed html parser infinite loop on certain malformed - source code.
  • -
  • #3639 : Button size was inconsistent.
  • -
  • #3874 : Paste as plain text in Safari loosing lines.
  • -
  • #3849 : Fixed IE8 crashes when applying lists and indenting.
  • -
  • #3876 : Changed dialog checkbox and radio labels to explicit labels.
  • -
  • #3843 : Fixed context submenu position in IE 6 & 7 RTL.
  • -
  • #3864 : [FF]Document is not editable after inserting element on a fresh page.
  • -
  • #3883 : Fixed removing inline style logic incorrect on Firefox2.
  • -
  • #3884 : Empty "href" attribute was duplicated on output data.
  • -
  • #3858 : Fixed the issue where toolbars - break up in IE6 and IE7 after the browser is resized.
  • -
  • #3868 : [chrome] SCAYT toolbar options was in reversed order.
  • -
  • #3875 : Fixed an issue in Safari where - table row/column/cell menus are not useable when table cells are selected.
  • -
  • #3896 : The editing area was - flashing when switching forth and back to source view.
  • -
  • #3894 : Fixed an issue where editor failed to initialize when using the on-demand loading way.
  • -
  • #3903 : Color button plugin doesn't read config entry from editor instance correctly.
  • -
  • #3801 : Comments at the start of the document was lost in IE.
  • -
  • #3871 : Unable to redo when undos to the front of snapshots stack.
  • -
  • #3909 : Move focus from editor into a text input control is broken.
  • -
  • #3870 : The empty paragraph - desappears when hitting ENTER after "New Page".
  • -
  • #3887 : Fixed an issue in which the create - list command may leak outside of a selected table cell and into the rest of document.
  • -
  • #3916 : Fixed maximize does not enlarge editor width when width is set.
  • -
  • #3879 : [webkit] Color button panel had incorrect size on first open.
  • -
  • #3839 : Update Scayt plugin to reflect the latest change from SpellChecker.net.
  • -
  • #3742 : Fixed wrong dialog layout for dialogs without tab bar in IE RTL mode .
  • -
  • #3671 : Fixed body fixing should be applied to the real type under fake elements.
  • -
  • #3836 : Fixed remove list in enterMode=BR will merge sibling text to one line.
  • -
  • #3949 : Fixed enterKey within pre-formatted text introduce wrong line-break.
  • -
  • #3878 : Whenever possible, - dialogs will not present scrollbars if the content is too big for its standard - size.
  • -
  • #3782 : Remove empty list in table cell result in collapsed cell.
  • -
  • Updated the following language files:
  • -
  • #3984 : [IE]The pre-formatted style is generating error.
  • -
  • #3946 : Fixed unable to hide contextmenu.
  • -
  • #3956 : Fixed About dialog in Source Mode for IE.
  • -
  • #3953 : Fixed keystroke for close Paste dialog.
  • -
  • #3951 : Reset size and lock ratio options were not accessible in Image dialog.
  • -
  • #3921 : Fixed Container scroll issue on IE7.
  • -
  • #3940 : Fixed list operation doesn't stop at table.
  • -
  • #3891 : [IE] Fixed 'automatic' font color doesn't work.
  • -
  • #3972 : Fixed unable to remove a single empty list in document in Firefox with enterMode=BR.
  • -
  • #3973 : Fixed list creation error at the end of document.
  • -
  • #3959 : Pasting styled text from word result in content lost.
  • -
  • #3793 : Combined images into sprites.
  • -
  • #3783 : Fixed indenting command in table cells create collapsed paragraph.
  • -
  • #3968 : About dialog layout was broken with IE+Standards+RTL.
  • -
  • #3991 : In IE quirks, text was not visible in v2 and office2003 skins.
  • -
  • #3983 : In IE, we'll now - silently ignore wrong toolbar definition settings which have extra commas being - left around.
  • -
  • Fixed the following test cases:
      -
    • #3992 : core/ckeditor2.html
    • -
    • #4138 : core/plugins.html
    • -
    • #3801 : plugins/htmldataprocessor/htmldataprocessor.html
    • -
  • -
  • #3989 : Host page horizontal scrolling a lot when on having righ-to-left direction.
  • -
  • #4001 : Create link around existing image result incorrect.
  • -
  • #3988 : Destroy editor on form submit event cause error.
  • -
  • #3994 : Insert horizontal line at end of document cause error.
  • -
  • #4074 : Indent error with 'indentClasses' config specified.
  • -
  • #4057 : Fixed anchor is lost after switch between editing modes.
  • -
  • #3644 : Image dialog was missin radio lock.
  • -
  • #4014 : Firefox2 had no dialog button backgrounds.
  • -
  • #4018 : Firefox2 had no richcombo text visible.
  • -
  • #4035 : [IE6] Paste dialog size was too small.
  • -
  • #4049 : Kama skin was too wide with config.width.
  • -
  • The following released files now doesn't require the _source folder
      -
    • #4086 : _samples/ui_languages.html
    • -
    • #4093 : _tests/core/dom/document.html
    • -
    • #4094 : Smiley plugin file
    • -
    • #4097 : No undo/redo support for fontColor and backgroundColor buttons.
    • -
  • -
  • #4085 : Paste and Paste from Word dialogs were not well styled in IE+RTL.
  • -
  • #3982 : Fixed enterKey on empty list item result in weird dom structure.
  • -
  • #4101 : Now it is possible to close dialog before gets focus.
  • -
  • #4075 : [IE6/7]Fixed apply custom inline style with "class" attribute failed.
  • -
  • #4087 : [Firefox]Fixed extra blocks created on create list when full document selected.
  • -
  • #4097 : No undo/redo support for fontColor and backgroundColor buttons.
  • -
  • #4111 : Fixed apply block style after inline style applied on full document error.
  • -
  • #3622 : Fixed shift enter with selection not deleting highlighted text.
  • -
  • #4092 : [IE6] Close button was missing for dialog without multiple tabs.
  • -
  • #4003 : Markup on the image dialog was disrupted when removing the border input.
  • -
  • #4096 : Editor content area was pushed down in IE RTL quirks.
  • -
  • #4112 : [FF] Paste dialog had scrollbars in quirks.
  • -
  • #4118 : Dialog dragging was - occasionally behaving strangely .
  • -
  • #4077 : The toolbar combos - were rendering incorrectly in some languages, like Chinese.
  • -
  • #3622 : The toolbar in the v2 - skin was wrapping improperly in some languages.
  • -
  • #4119 : Unable to edit image link with image dialog.
  • -
  • #4117 : Fixed dialog error when transforming image into button.
  • -
  • #4058 : [FF] wysiwyg mode is sometimes not been activated.
  • -
  • #4114 : [IE] RTE + IE6/IE7 Quirks = dialog mispositoned.
  • -
  • #4123 : Some dialog buttons were broken in IE7 quirks.
  • -
  • #4122 : [IE] The image dialog - was being rendered improperly when loading an image with long URL.
  • -
  • #4144 : Fixed the white-spaces at the end of <pre> is incorrectly removed.
  • -
  • #4143 : Fixed element id is lost when extracting contents from the range.
  • -
  • #4007 : [IE] Source area overflow from editor chrome.
  • -
  • #4145 : Fixed the on demand - ("basic") loading model of the editor.
  • -
  • #4139 : Fixed list plugin regression of [3903].
  • -
  • #4147 : Unify style text normalization logic when comparing styles.
  • -
  • #4150 : Fixed enlarge list result incorrect at the inner boundary of block.
  • -
  • #4164 : Now it is possible to paste text - in Source mode even if forcePasteAsPlainText = true.
  • -
  • #4129 : [FF]Unable to remove list with Ctrl-A.
  • -
  • #4172 : [Safari] The trailing - <br> was not been always added to blank lines ending with &nbsp;.
  • -
  • #4178 : It's now possible to - copy and paste Flash content among different editor instances.
  • -
  • #4193 : Automatic font color produced empty span on Firefox 3.5.
  • -
  • #4186 : [FF] Fixed First open float panel cause host page scrollbar blinking.
  • -
  • #4227 : Fixed destroy editor instance created on textarea which is not within form cause error.
  • -
  • #4240 : Fixed editor name containing hyphen break editor completely.
  • -
  • #3828 : Malformed nested list is now corrected by the parser.
  • -
-

- CKEditor 3.0 RC

-

- Changelog starts at this release.

- - - diff --git a/ckeditor/CHANGES.md b/ckeditor/CHANGES.md deleted file mode 100644 index 6a6e62f3a..000000000 --- a/ckeditor/CHANGES.md +++ /dev/null @@ -1,1118 +0,0 @@ -CKEditor 4 Changelog -==================== - -## CKEditor 4.5.11 - -**Security Updates:** - -* [Severity: minor] Fixed the target="_blank" vulnerability reported by James Gaskell. - - Issue summary: If a victim had access to a spoofed version of ckeditor.com via HTTP (e.g. due to DNS spoofing, using a hacked public network or mailicious hotspot), then when using a link to the ckeditor.com website it was possible for the attacker to change the current URL of the opening page, even if the opening page was protected with SSL. - - An upgrade is recommended. - -New Features: - -* [#14747](http://dev.ckeditor.com/ticket/14747): The [Enhanced Image](http://ckeditor.com/addon/image2) caption now supports the link `target` attribute. -* [#7154](http://dev.ckeditor.com/ticket/7154): Added support for the "Display Text" field to the [Link](http://ckeditor.com/addon/link) dialog. Thanks to [Ryan Guill](https://github.com/ryanguill)! - -Fixed Issues: - -* [#13362](http://dev.ckeditor.com/ticket/13362): [Blink, WebKit] Fixed: Active widget element is not cached when it is losing focus and it is inside an editable element. -* [#13755](http://dev.ckeditor.com/ticket/13755): [Edge] Fixed: Pasting images does not work. -* [#13548](http://dev.ckeditor.com/ticket/13548): [IE] Fixed: Clicking the [elements path](http://ckeditor.com/addon/elementspath) disables Cut and Copy icons. -* [#13812](http://dev.ckeditor.com/ticket/13812): Fixed: When aborting file upload the placeholder for image is left. -* [#14659](http://dev.ckeditor.com/ticket/14659): [Blink] Fixed: Content scrolled to the top after closing the dialog in a [`
`-based editor](http://ckeditor.com/addon/divarea). -* [#14825](http://dev.ckeditor.com/ticket/14825): [Edge] Fixed: Focusing the editor causes unwanted scrolling due to dropped support for the `setActive` method. - -## CKEditor 4.5.10 - -Fixed Issues: - -* [#10750](http://dev.ckeditor.com/ticket/10750): Fixed: The editor does not escape the `font-style` family property correctly, removing quotes and whitespace from font names. -* [#14413](http://dev.ckeditor.com/ticket/14413): Fixed: The [Auto Grow](http://ckeditor.com/addon/autogrow) plugin with the [`config.autoGrow_onStartup`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-autoGrow_onStartup) option set to `true` does not work properly for an editor that is not visible. -* [#14451](http://dev.ckeditor.com/ticket/14451): Fixed: Numeric element ID not escaped properly. Thanks to [Jakub Chalupa](https://github.com/chaluja7)! -* [#14590](http://dev.ckeditor.com/ticket/14590): Fixed: Additional line break appearing after inline elements when switching modes. Thanks to [dpidcock](https://github.com/dpidcock)! -* [#14539](https://dev.ckeditor.com/ticket/14539): Fixed: JAWS reads "selected Blank" instead of "selected " when selecting a widget. -* [#14701](http://dev.ckeditor.com/ticket/14701): Fixed: More precise labels for [Enhanced Image](http://ckeditor.com/addon/image2) and [Placeholder](http://ckeditor.com/addon/placeholder) widgets. -* [#14667](http://dev.ckeditor.com/ticket/14667): [IE] Fixed: Removing background color from selected text removes background color from the whole paragraph. -* [#14252](http://dev.ckeditor.com/ticket/14252): [IE] Fixed: Styles drop-down list does not always reflect the current style of the text line. -* [#14275](http://dev.ckeditor.com/ticket/14275): [IE9+] Fixed: `onerror` and `onload` events are not used in browsers it could have been used when loading scripts dynamically. - -## CKEditor 4.5.9 - -Fixed Issues: - -* [#10685](http://dev.ckeditor.com/ticket/10685): Fixed: Unreadable toolbar icons after updating to the new editor version. Fixed with [6876179](https://github.com/ckeditor/ckeditor-dev/commit/6876179db4ee97e786b07b8fd72e6b4120732185) in [ckeditor-dev](https://github.com/ckeditor/ckeditor-dev) and [6c9189f4](https://github.com/ckeditor/ckeditor-presets/commit/6c9189f46392d2c126854fe8889b820b8c76d291) in [ckeditor-presets](https://github.com/ckeditor/ckeditor-presets). -* [#14573](https://dev.ckeditor.com/ticket/14573): Fixed: Missing [Widget](http://ckeditor.com/addon/widget) drag handler CSS when there are multiple editor instances. -* [#14620](https://dev.ckeditor.com/ticket/14620): Fixed: Setting both the `min-height` style for the `` element and the `height` style for the `` element breaks the [Auto Grow](http://ckeditor.com/addon/autogrow) plugin. -* [#14538](http://dev.ckeditor.com/ticket/14538): Fixed: Keyboard focus goes into an embedded `