From b595b30a5bfc7c3d8783b8c3a0307c89e8b571e6 Mon Sep 17 00:00:00 2001 From: Lucas Marcouiller <45882981+Hystepik@users.noreply.github.com> Date: Fri, 4 Oct 2024 09:00:17 +0200 Subject: [PATCH] New create public page for recruitment candidature (#31220) * New creation of public recruitement candidature * Fix some problems * fix lang file * Fix CI * Fix CI --------- Co-authored-by: Hystepik Co-authored-by: Laurent Destailleur --- htdocs/core/lib/public.lib.php | 6 +- .../install/mysql/migration/20.0.0-21.0.0.sql | 3 + ...ent_recruitmentcandidature-recruitment.sql | 2 +- htdocs/langs/en_US/recruitment.lang | 3 + htdocs/public/recruitment/index.php | 13 ++ htdocs/public/recruitment/view.php | 170 +++++++++++++----- .../class/recruitmentcandidature.class.php | 2 +- 7 files changed, 151 insertions(+), 48 deletions(-) diff --git a/htdocs/core/lib/public.lib.php b/htdocs/core/lib/public.lib.php index 0ce710517de..47a3f0e4209 100644 --- a/htdocs/core/lib/public.lib.php +++ b/htdocs/core/lib/public.lib.php @@ -52,7 +52,11 @@ function checkNbPostsForASpeceificIp($object, $nb_post_max) $sql = "SELECT COUNT(".(!empty($object->table_rowid) ? $object->table_rowid : 'rowid').") as nb_posts"; $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element; $sql .= " WHERE ip = '".$db->escape($object->ip)."'"; - $sql .= " AND datec > '".$db->idate($minmonthpost)."'"; + if (array_key_exists('date_creation', $object->fields)) { + $sql .= " AND date_creation > '".$db->idate($minmonthpost)."'"; + } else { + $sql .= " AND datec > '".$db->idate($minmonthpost)."'"; + } $resql = $db->query($sql); if ($resql) { $num = $db->num_rows($resql); diff --git a/htdocs/install/mysql/migration/20.0.0-21.0.0.sql b/htdocs/install/mysql/migration/20.0.0-21.0.0.sql index 33b86a4d8df..ba603342da7 100644 --- a/htdocs/install/mysql/migration/20.0.0-21.0.0.sql +++ b/htdocs/install/mysql/migration/20.0.0-21.0.0.sql @@ -132,4 +132,7 @@ ALTER TABLE llx_societe ADD COLUMN ip varchar(250); ALTER TABLE llx_recruitment_recruitmentcandidature ADD COLUMN ip varchar(250); ALTER TABLE llx_socpeople ADD COLUMN ip varchar(250); +ALTER TABLE llx_recruitment_recruitmentcandidature MODIFY fk_user_creat integer NULL; + ALTER TABLE llx_ecm_files ADD COLUMN agenda_id integer; + diff --git a/htdocs/install/mysql/tables/llx_recruitment_recruitmentcandidature-recruitment.sql b/htdocs/install/mysql/tables/llx_recruitment_recruitmentcandidature-recruitment.sql index 8016e813d5d..af174cc2568 100644 --- a/htdocs/install/mysql/tables/llx_recruitment_recruitmentcandidature-recruitment.sql +++ b/htdocs/install/mysql/tables/llx_recruitment_recruitmentcandidature-recruitment.sql @@ -25,7 +25,7 @@ CREATE TABLE llx_recruitment_recruitmentcandidature( note_private text, date_creation datetime NOT NULL, tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - fk_user_creat integer NOT NULL, + fk_user_creat integer, fk_user_modif integer, fk_user integer, -- Id of user created from the job application import_key varchar(14), diff --git a/htdocs/langs/en_US/recruitment.lang b/htdocs/langs/en_US/recruitment.lang index 047455d5e2b..3d249271e2b 100644 --- a/htdocs/langs/en_US/recruitment.lang +++ b/htdocs/langs/en_US/recruitment.lang @@ -64,4 +64,7 @@ NoPositionOpen=No positions open at the moment ConfirmClose=Confirm cancellation ConfirmCloseAsk=Are you sure you want to cancel this recruitment candidature Recruitment=Recruitment +ApplyJobCandidature=Apply for this job +ErrorRecruitmmentCandidatureAlreadyExists= Recruitment candidature already exists with email %s +RecruitmentCandidatureSaved=Recruitement candidature successfully saved NoOpenJobOffer=No open job offers at the moment diff --git a/htdocs/public/recruitment/index.php b/htdocs/public/recruitment/index.php index af86d85034b..bf69fda17cc 100644 --- a/htdocs/public/recruitment/index.php +++ b/htdocs/public/recruitment/index.php @@ -174,6 +174,7 @@ if (getDolGlobalString('RECRUITMENT_IMAGE_PUBLIC_INTERFACE')) { $results = $object->fetchAll($sortorder, $sortfield, 0, 0, '(status:=:1)'); $now = dol_now(); +$params = array(); if (is_array($results)) { if (empty($results)) { @@ -187,6 +188,7 @@ if (is_array($results)) { foreach ($results as $job) { $object = $job; + $arrayofpostulatebutton = array(); print ''."\n"; @@ -274,6 +276,17 @@ if (is_array($results)) { print $text; print ''; + $arrayofpostulatebutton[] = array( + 'url' => '/public/recruitment/view.php?ref='.$object->ref, + 'label' => $langs->trans('ApplyJobCandidature'), + 'lang' => 'recruitment', + 'perm' => true, + 'enabled' => true, + ); + + print '
'; + print dolGetButtonAction('', $langs->trans("ApplyJobCandidature"), 'default', $arrayofpostulatebutton, 'applicate_'.$object->ref, true, $params); + print '
'; print ''."\n"; print "\n"; diff --git a/htdocs/public/recruitment/view.php b/htdocs/public/recruitment/view.php index c3c205c2685..3a8e48f935e 100644 --- a/htdocs/public/recruitment/view.php +++ b/htdocs/public/recruitment/view.php @@ -38,10 +38,12 @@ if (!defined('NOBROWSERNOTIF')) { // Load Dolibarr environment require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/recruitment/class/recruitmentjobposition.class.php'; +require_once DOL_DOCUMENT_ROOT.'/recruitment/class/recruitmentcandidature.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/public.lib.php'; // Load translation files required by the page $langs->loadLangs(array("companies", "other", "recruitment")); @@ -50,7 +52,12 @@ $langs->loadLangs(array("companies", "other", "recruitment")); $action = GETPOST('action', 'aZ09'); $cancel = GETPOST('cancel', 'alpha'); $email = GETPOST('email', 'alpha'); -$backtopage = ''; +$firstname = GETPOST('firstname', 'alpha'); +$lastname = GETPOST('lastname', 'alpha'); +$birthday = GETPOST('birthday', 'alpha'); +$phone = GETPOST('phone', 'alpha'); +$message = GETPOST('message', 'alpha'); +$requestedremuneration = GETPOST('requestedremuneration', 'alpha'); $ref = GETPOST('ref', 'alpha'); @@ -63,25 +70,26 @@ if (isset($_SESSION['email_customer'])) { $object = new RecruitmentJobPosition($db); -if (!$action) { - if (!$ref) { - print $langs->trans('ErrorBadParameters')." - ref missing"; - exit; - } else { - $object->fetch('', $ref); - } +if (!$ref) { + print $langs->trans('ErrorBadParameters')." - ref missing"; + exit; } + // Define $urlwithroot //$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); //$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file $urlwithroot = DOL_MAIN_URL_ROOT; // This is to use same domain name than current. For Paypal payment, we can use internal URL like localhost. +$backtopage = $urlwithroot.'/public/recruitment/index.php'; // Security check -if (empty($conf->recruitment->enabled)) { +if (!isModEnabled("recruitment")) { httponly_accessforbidden('Module Recruitment not enabled'); } +$object->fetch('', $ref); +$user->loadDefaultValues(); +$errmsg = ""; /* * Actions @@ -92,56 +100,95 @@ if ($cancel) { header("Location: ".$backtopage); exit; } - $action = 'view'; } -if ($action == "view" || $action == "presend" || $action == "dosubmit") { // Test on permission not required here (anonymous action protected by mitigation of /public/... urls) +if ($action == "dosubmit") { // Test on permission not required here (anonymous action protected by mitigation of /public/... urls) $error = 0; - $display_ticket = false; + $db->begin(); if (!strlen($ref)) { $error++; array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Ref"))); - $action = ''; + $action = 'view'; } if (!strlen($email)) { $error++; array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Email"))); - $action = ''; + $action = 'view'; } else { if (!isValidEmail($email)) { $error++; array_push($object->errors, $langs->trans("ErrorEmailInvalid")); - $action = ''; + $action = 'view'; + } + } + if (!strlen($lastname)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Lastname"))); + $action = 'view'; + } + + if (!$error) { + $sql = "SELECT rrc.rowid FROM ".MAIN_DB_PREFIX."recruitment_recruitmentcandidature as rrc"; + $sql .= " WHERE rrc.email = '". $db->escape($email)."'"; + $sql .= " AND rrc.entity = ". getEntity($object->element, 0); + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + if ($num > 0) { + $error++; + setEventMessages($langs->trans("ErrorRecruitmmentCandidatureAlreadyExists", $email), null, 'errors'); + } + } else { + dol_print_error($db); + $error++; + } + } + + if (!$error) { // Test on permission not required here (anonymous action protected by mitigation of /public/... urls) + $candidature = new RecruitmentCandidature($db); + + $candidature->firstname = GETPOST('firstname', 'alpha'); + $candidature->lastname = GETPOST('lastname', 'alpha'); + $candidature->email = GETPOST('email', 'alpha'); + $candidature->phone = GETPOST('phone', 'alpha'); + $candidature->date_birth = GETPOST('birthday', 'alpha'); + $candidature->requestedremuneration = GETPOST('requestedremuneration', 'alpha'); + $candidature->description = GETPOST('message', 'alpha'); + $candidature->fk_recruitmentjobposition = $object->id; + + $candidature->ip = getUserRemoteIP(); + + // Test MAIN_SECURITY_MAX_POST_ON_PUBLIC_PAGES_BY_IP_ADDRESS + $nb_post_max = getDolGlobalInt("MAIN_SECURITY_MAX_POST_ON_PUBLIC_PAGES_BY_IP_ADDRESS", 200); + + if (checkNbPostsForASpeceificIp($candidature, $nb_post_max) <= 0) { + $error++; + $errmsg .= implode('
', $candidature->errors); + } + if (!$error) { + $result = $candidature->create($user); + if ($result <= 0) { + $error++; + $errmsg .= implode('
', $candidature->errors); + } + } + if (!$error) { + $candidature->validate($user); + if ($result <= 0) { + $error++; + $errmsg .= implode('
', $candidature->errors); + } } } if (!$error) { - $ret = $object->fetch('', $ref); - } - - /* - if (!$error && $action == "dosubmit") // Test on permission not required here (anonymous action protected by mitigation of /public/... urls) - { - // Test MAIN_SECURITY_MAX_POST_ON_PUBLIC_PAGES_BY_IP_ADDRESS - - // TODO Create job application - - - - if (!$error) - { - $action = 'view'; - } - } - */ - - if ($error || $errors) { - setEventMessages($object->error, $object->errors, 'errors'); - if ($action == "dosubmit") { // Test on permission not required here - $action = 'presend'; - } else { - $action = ''; - } + $db->commit(); + setEventMessages($langs->trans("RecruitmentCandidatureSaved"), null); + header("Location: " . $backtopage); + exit; + } else { + $db->rollback(); + $action = "view"; } } @@ -158,6 +205,7 @@ include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php'; * View */ +$form = new Form($db); $now = dol_now(); $head = ''; @@ -180,7 +228,7 @@ $arrayofcss = array(); $replacemainarea = (empty($conf->dol_hide_leftmenu) ? '
' : '').'
'; llxHeader($head, $langs->trans("PositionToBeFilled"), '', '', 0, 0, '', '', '', 'onlinepaymentbody', $replacemainarea, 1, 1); - +dol_htmloutput_errors($errmsg); print ''."\n"; print '
'."\n"; @@ -254,16 +302,16 @@ if (getDolGlobalString('RECRUITMENT_NEWFORM_TEXT')) { $text = '
'."\n"; } if (empty($text)) { - $text .= ''."\n"; - $text .= ''."\n"; + $text .= ''."\n"; } print $text; // Output payment summary form -print ''."\n"; + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + + print ''."\n"; } else { dol_print_error_email('ERRORSUBMITAPPLICATION'); } diff --git a/htdocs/recruitment/class/recruitmentcandidature.class.php b/htdocs/recruitment/class/recruitmentcandidature.class.php index e217f3489ae..b30819536c3 100644 --- a/htdocs/recruitment/class/recruitmentcandidature.class.php +++ b/htdocs/recruitment/class/recruitmentcandidature.class.php @@ -105,7 +105,7 @@ class RecruitmentCandidature extends CommonObject 'fk_recruitmentjobposition' => array('type' => 'integer:RecruitmentJobPosition:recruitment/class/recruitmentjobposition.class.php:0:(t.status:=:1)', 'label' => 'Job', 'enabled' => 1, 'position' => 15, 'notnull' => 0, 'visible' => 1, 'index' => 1, 'picto' => 'recruitmentjobposition', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist' => 'minwidth100 nowraponall'), 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'position' => 61, 'notnull' => 0, 'visible' => 0,), 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 62, 'notnull' => 0, 'visible' => 0,), - 'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'position' => 510, 'notnull' => 1, 'visible' => -2, 'foreignkey' => 'user.rowid', 'csslist' => 'tdoverflowmax100'), + 'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'position' => 510, 'notnull' => -1, 'visible' => -2, 'foreignkey' => 'user.rowid', 'csslist' => 'tdoverflowmax100'), 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'position' => 511, 'notnull' => -1, 'visible' => -2, 'csslist' => 'tdoverflowmax100'), 'lastname' => array('type' => 'varchar(128)', 'label' => 'Lastname', 'enabled' => 1, 'position' => 20, 'notnull' => 0, 'visible' => 1, 'csslist' => 'tdoverflowmax150'), 'firstname' => array('type' => 'varchar(128)', 'label' => 'Firstname', 'enabled' => 1, 'position' => 21, 'notnull' => 0, 'visible' => 1, 'csslist' => 'tdoverflowmax150'),

'.$text.'

'.$langs->trans("JobOfferToBeFilled", $mysoc->name); + $text .= '

'.$langs->trans("JobOfferToBeFilled", $mysoc->name); $text .= '   -   '.$mysoc->name.''; $text .= '   -   '.dol_print_date($object->date_creation).''; $text .= '

'.$object->label.'


'.$object->label.'


'; +print '
'; print '
'; print '
'.$langs->trans("ThisIsInformationOnJobPosition").' :
'."\n"; @@ -333,6 +381,38 @@ print "\n"; if ($action != 'dosubmit') { if ($found && !$error) { // We are in a management option and no error + print '
'.$langs->trans("Lastname").''; + print ''; + print '
'.$langs->trans("Firstname").''; + print ''; + print '
'.$langs->trans("Email").''; + print img_picto("", "email").''; + print '
'.$langs->trans("Phone").''; + print img_picto("", "phone").''; + print '
'.$langs->trans("DateOfBirth").''; + print $form->selectDate($birthday, 'birthday', 0, 0, 1, "", 1, 0); + print '
'.$langs->trans("RequestedRemuneration").''; + print ''; + print '
'.$langs->trans("Message").''; + print ''; + print '
'; + print $form->buttonsSaveCancel('Submit', 'Cancel'); + print '