1<?php 2/* Copyright (C) 2020 Laurent Destailleur <eldy@users.sourceforge.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18/** 19 * \file class/recruitmentcandidature.class.php 20 * \ingroup recruitment 21 * \brief This file is a CRUD class file for RecruitmentCandidature (Create/Read/Update/Delete) 22 */ 23 24// Put here all includes required by your class file 25require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; 26//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; 27//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; 28 29/** 30 * Class for RecruitmentCandidature 31 */ 32class RecruitmentCandidature extends CommonObject 33{ 34 /** 35 * @var string ID of module. 36 */ 37 public $module = 'recruitment'; 38 39 /** 40 * @var string ID to identify managed object. 41 */ 42 public $element = 'recruitmentcandidature'; 43 44 /** 45 * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management. 46 */ 47 public $table_element = 'recruitment_recruitmentcandidature'; 48 49 /** 50 * @var int Does this object support multicompany module ? 51 * 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table 52 */ 53 public $ismultientitymanaged = 0; 54 55 /** 56 * @var int Does object support extrafields ? 0=No, 1=Yes 57 */ 58 public $isextrafieldmanaged = 1; 59 60 /** 61 * @var string String with name of icon for recruitmentcandidature. Must be the part after the 'object_' into object_recruitmentcandidature.png 62 */ 63 public $picto = 'recruitmentcandidature'; 64 65 66 const STATUS_DRAFT = 0; 67 const STATUS_VALIDATED = 1; 68 //const STATUS_INTERVIEW_SCHEDULED = 2; 69 const STATUS_CONTRACT_PROPOSED = 3; 70 const STATUS_CONTRACT_SIGNED = 5; 71 const STATUS_CONTRACT_REFUSED = 6; 72 const STATUS_REFUSED = 8; 73 const STATUS_CANCELED = 9; 74 75 76 /** 77 * 'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password') 78 * Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)" 79 * 'label' the translation key. 80 * 'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM) 81 * 'position' is the sort order of field. 82 * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). 83 * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) 84 * 'noteditable' says if field is not editable (1 or 0) 85 * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. 86 * 'index' if we want an index in database. 87 * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). 88 * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. 89 * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). 90 * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'maxwidth200', 'wordbreak', 'tdoverflowmax200' 91 * 'help' is a string visible as a tooltip on field 92 * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record 93 * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. 94 * 'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") 95 * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. 96 * 'comment' is not used. You can store here any text of your choice. It is not used by application. 97 * 98 * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. 99 */ 100 101 // BEGIN MODULEBUILDER PROPERTIES 102 /** 103 * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. 104 */ 105 public $fields = array( 106 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'comment'=>"Id"), 107 'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'position'=>5, 'notnull'=>1, 'default'=>'1', 'index'=>1), 108 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>'1', 'position'=>10, 'notnull'=>1, 'visible'=>4, 'noteditable'=>'1', 'default'=>'(PROV)', 'index'=>1, 'searchall'=>1, 'showoncombobox'=>'1', 'comment'=>"Reference of candidature"), 109 'fk_recruitmentjobposition' => array('type'=>'integer:RecruitmentJobPosition:recruitment/class/recruitmentjobposition.class.php', 'label'=>'Job', 'enabled'=>'1', 'position'=>15, 'notnull'=>0, 'visible'=>1, 'index'=>1), 110 'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>'1', 'position'=>61, 'notnull'=>0, 'visible'=>0,), 111 'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>'1', 'position'=>62, 'notnull'=>0, 'visible'=>0,), 112 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>'1', 'position'=>500, 'notnull'=>1, 'visible'=>-2,), 113 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>'1', 'position'=>501, 'notnull'=>0, 'visible'=>-2,), 114 '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',), 115 'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>'1', 'position'=>511, 'notnull'=>-1, 'visible'=>-2,), 116 'lastname' => array('type'=>'varchar(128)', 'label'=>'Lastname', 'enabled'=>'1', 'position'=>20, 'notnull'=>0, 'visible'=>1,), 117 'firstname' => array('type'=>'varchar(128)', 'label'=>'Firstname', 'enabled'=>'1', 'position'=>21, 'notnull'=>0, 'visible'=>1,), 118 'email' => array('type'=>'varchar(255)', 'label'=>'EMail', 'enabled'=>'1', 'position'=>30, 'notnull'=>1, 'visible'=>1, 'picto'=>'email'), 119 'phone' => array('type'=>'varchar(64)', 'label'=>'Phone', 'enabled'=>'1', 'position'=>31, 'notnull'=>0, 'visible'=>1,), 120 'date_birth' => array('type'=>'date', 'label'=>'DateOfBirth', 'enabled'=>'1', 'position'=>70, 'visible'=>-1,), 121 'email_msgid' => array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'help'=>'EmailMsgIDDesc'), 122 //'fk_recruitment_origin' => array('type'=>'integer:CRecruitmentOrigin:recruitment/class/crecruitmentorigin.class.php', 'label'=>'Origin', 'enabled'=>'1', 'position'=>45, 'visible'=>1, 'index'=>1), 123 'remuneration_requested' => array('type'=>'integer', 'label'=>'RequestedRemuneration', 'enabled'=>'1', 'position'=>80, 'notnull'=>0, 'visible'=>-1,), 124 'remuneration_proposed' => array('type'=>'integer', 'label'=>'ProposedRemuneration', 'enabled'=>'1', 'position'=>81, 'notnull'=>0, 'visible'=>-1,), 125 'description' => array('type'=>'html', 'label'=>'Description', 'enabled'=>'1', 'position'=>500, 'notnull'=>0, 'visible'=>3,), 126 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>'1', 'position'=>1000, 'notnull'=>-1, 'visible'=>-2,), 127 'model_pdf' => array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>'1', 'position'=>1010, 'notnull'=>-1, 'visible'=>0,), 128 'status' => array('type'=>'smallint', 'label'=>'Status', 'enabled'=>'1', 'position'=>1000, 'notnull'=>1, 'visible'=>1, 'index'=>1, 'default'=>0, 'arrayofkeyval'=>array('0'=>'Draft', '1'=>'Received', '3'=>'ContractProposed', '5'=>'ContractSigned', '8'=>'Refused', '9'=>'Canceled')), 129 ); 130 public $rowid; 131 public $entity; 132 public $ref; 133 public $fk_recruitmentjobposition; 134 public $description; 135 public $note_public; 136 public $note_private; 137 public $date_creation; 138 public $tms; 139 public $fk_user_creat; 140 public $fk_user_modif; 141 public $lastname; 142 public $firstname; 143 public $email; 144 public $phone; 145 public $date_birth; 146 public $email_msgid; 147 public $remuneration_requested; 148 public $remuneration_proposed; 149 public $fk_recruitment_origin; 150 public $import_key; 151 public $model_pdf; 152 public $status; 153 // END MODULEBUILDER PROPERTIES 154 155 156 /** 157 * Constructor 158 * 159 * @param DoliDb $db Database handler 160 */ 161 public function __construct(DoliDB $db) 162 { 163 global $conf, $langs; 164 165 $this->db = $db; 166 167 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible'] = 0; 168 if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled'] = 0; 169 170 // Example to show how to set values of fields definition dynamically 171 /*if ($user->rights->recruitment->recruitmentcandidature->read) { 172 $this->fields['myfield']['visible'] = 1; 173 $this->fields['myfield']['noteditable'] = 0; 174 }*/ 175 176 // Unset fields that are disabled 177 foreach ($this->fields as $key => $val) 178 { 179 if (isset($val['enabled']) && empty($val['enabled'])) 180 { 181 unset($this->fields[$key]); 182 } 183 } 184 185 // Translate some data of arrayofkeyval 186 if (is_object($langs)) 187 { 188 foreach ($this->fields as $key => $val) 189 { 190 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) 191 { 192 foreach ($val['arrayofkeyval'] as $key2 => $val2) 193 { 194 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2); 195 } 196 } 197 } 198 } 199 } 200 201 /** 202 * Create object into database 203 * 204 * @param User $user User that creates 205 * @param bool $notrigger false=launch triggers after, true=disable triggers 206 * @return int <0 if KO, Id of created object if OK 207 */ 208 public function create(User $user, $notrigger = false) 209 { 210 return $this->createCommon($user, $notrigger); 211 } 212 213 /** 214 * Clone an object into another one 215 * 216 * @param User $user User that creates 217 * @param int $fromid Id of object to clone 218 * @return mixed New object created, <0 if KO 219 */ 220 public function createFromClone(User $user, $fromid) 221 { 222 global $langs, $extrafields; 223 $error = 0; 224 225 dol_syslog(__METHOD__, LOG_DEBUG); 226 227 $object = new self($this->db); 228 229 $this->db->begin(); 230 231 // Load source object 232 $result = $object->fetchCommon($fromid); 233 if ($result > 0 && !empty($object->table_element_line)) $object->fetchLines(); 234 235 // get lines so they will be clone 236 //foreach($this->lines as $line) 237 // $line->fetch_optionals(); 238 239 // Reset some properties 240 unset($object->id); 241 unset($object->fk_user_creat); 242 unset($object->import_key); 243 244 // Clear fields 245 if (property_exists($object, 'ref')) $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default']; 246 if (property_exists($object, 'label')) $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default']; 247 if (property_exists($object, 'status')) { $object->status = self::STATUS_DRAFT; } 248 if (property_exists($object, 'date_creation')) { $object->date_creation = dol_now(); } 249 if (property_exists($object, 'date_modification')) { $object->date_modification = null; } 250 251 // ... 252 // Clear extrafields that are unique 253 if (is_array($object->array_options) && count($object->array_options) > 0) 254 { 255 $extrafields->fetch_name_optionals_label($this->table_element); 256 foreach ($object->array_options as $key => $option) 257 { 258 $shortkey = preg_replace('/options_/', '', $key); 259 if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) 260 { 261 //var_dump($key); var_dump($clonedObj->array_options[$key]); exit; 262 unset($object->array_options[$key]); 263 } 264 } 265 } 266 267 // Create clone 268 $object->context['createfromclone'] = 'createfromclone'; 269 $result = $object->createCommon($user); 270 if ($result < 0) { 271 $error++; 272 $this->error = $object->error; 273 $this->errors = $object->errors; 274 } 275 276 if (!$error) 277 { 278 // copy internal contacts 279 if ($this->copy_linked_contact($object, 'internal') < 0) 280 { 281 $error++; 282 } 283 } 284 285 if (!$error) 286 { 287 // copy external contacts if same company 288 if (property_exists($this, 'socid') && $this->socid == $object->socid) 289 { 290 if ($this->copy_linked_contact($object, 'external') < 0) 291 $error++; 292 } 293 } 294 295 unset($object->context['createfromclone']); 296 297 // End 298 if (!$error) { 299 $this->db->commit(); 300 return $object; 301 } else { 302 $this->db->rollback(); 303 return -1; 304 } 305 } 306 307 /** 308 * Load object in memory from the database 309 * 310 * @param int $id Id object 311 * @param string $ref Ref 312 * @param string $email_msgid Email msgid 313 * @return int <0 if KO, 0 if not found, >0 if OK 314 */ 315 public function fetch($id, $ref = null, $email_msgid = '') 316 { 317 $morewhere = ''; 318 if ($email_msgid) $morewhere = " AND email_msgid = '".$this->db->escape($email_msgid)."'"; 319 $result = $this->fetchCommon($id, $ref, $morewhere); 320 if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines(); 321 return $result; 322 } 323 324 /** 325 * Load object lines in memory from the database 326 * 327 * @return int <0 if KO, 0 if not found, >0 if OK 328 */ 329 public function fetchLines() 330 { 331 $this->lines = array(); 332 333 $result = $this->fetchLinesCommon(); 334 return $result; 335 } 336 337 338 /** 339 * Load list of objects in memory from the database. 340 * 341 * @param string $sortorder Sort Order 342 * @param string $sortfield Sort field 343 * @param int $limit limit 344 * @param int $offset Offset 345 * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customurl'=>...) 346 * @param string $filtermode Filter mode (AND or OR) 347 * @return array|int int <0 if KO, array of pages if OK 348 */ 349 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND') 350 { 351 global $conf; 352 353 dol_syslog(__METHOD__, LOG_DEBUG); 354 355 $records = array(); 356 357 $sql = 'SELECT '; 358 $sql .= $this->getFieldList(); 359 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; 360 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')'; 361 else $sql .= ' WHERE 1 = 1'; 362 // Manage filter 363 $sqlwhere = array(); 364 if (count($filter) > 0) { 365 foreach ($filter as $key => $value) { 366 if ($key == 't.rowid') { 367 $sqlwhere[] = $key.'='.$value; 368 } elseif (in_array($this->fields[$key]['type'], array('date', 'datetime', 'timestamp'))) { 369 $sqlwhere[] = $key.' = \''.$this->db->idate($value).'\''; 370 } elseif ($key == 'customsql') { 371 $sqlwhere[] = $value; 372 } elseif (strpos($value, '%') === false) { 373 $sqlwhere[] = $key.' IN ('.$this->db->sanitize($this->db->escape($value)).')'; 374 } else { 375 $sqlwhere[] = $key.' LIKE \'%'.$this->db->escape($value).'%\''; 376 } 377 } 378 } 379 if (count($sqlwhere) > 0) { 380 $sql .= ' AND ('.implode(' '.$filtermode.' ', $sqlwhere).')'; 381 } 382 383 if (!empty($sortfield)) { 384 $sql .= $this->db->order($sortfield, $sortorder); 385 } 386 if (!empty($limit)) { 387 $sql .= ' '.$this->db->plimit($limit, $offset); 388 } 389 390 $resql = $this->db->query($sql); 391 if ($resql) { 392 $num = $this->db->num_rows($resql); 393 $i = 0; 394 while ($i < ($limit ? min($limit, $num) : $num)) 395 { 396 $obj = $this->db->fetch_object($resql); 397 398 $record = new self($this->db); 399 $record->setVarsFromFetchObj($obj); 400 401 $records[$record->id] = $record; 402 403 $i++; 404 } 405 $this->db->free($resql); 406 407 return $records; 408 } else { 409 $this->errors[] = 'Error '.$this->db->lasterror(); 410 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR); 411 412 return -1; 413 } 414 } 415 416 /** 417 * Update object into database 418 * 419 * @param User $user User that modifies 420 * @param bool $notrigger false=launch triggers after, true=disable triggers 421 * @return int <0 if KO, >0 if OK 422 */ 423 public function update(User $user, $notrigger = false) 424 { 425 return $this->updateCommon($user, $notrigger); 426 } 427 428 /** 429 * Delete object in database 430 * 431 * @param User $user User that deletes 432 * @param bool $notrigger false=launch triggers after, true=disable triggers 433 * @return int <0 if KO, >0 if OK 434 */ 435 public function delete(User $user, $notrigger = false) 436 { 437 return $this->deleteCommon($user, $notrigger); 438 //return $this->deleteCommon($user, $notrigger, 1); 439 } 440 441 /** 442 * Delete a line of object in database 443 * 444 * @param User $user User that delete 445 * @param int $idline Id of line to delete 446 * @param bool $notrigger false=launch triggers after, true=disable triggers 447 * @return int >0 if OK, <0 if KO 448 */ 449 public function deleteLine(User $user, $idline, $notrigger = false) 450 { 451 if ($this->status < 0) 452 { 453 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus'; 454 return -2; 455 } 456 457 return $this->deleteLineCommon($user, $idline, $notrigger); 458 } 459 460 461 /** 462 * Validate object 463 * 464 * @param User $user User making status change 465 * @param int $notrigger 1=Does not execute triggers, 0= execute triggers 466 * @return int <=0 if OK, 0=Nothing done, >0 if KO 467 */ 468 public function validate($user, $notrigger = 0) 469 { 470 global $conf, $langs; 471 472 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 473 474 $error = 0; 475 476 // Protection 477 if ($this->status == self::STATUS_VALIDATED) 478 { 479 dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING); 480 return 0; 481 } 482 483 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->recruitmentcandidature->write)) 484 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->recruitmentcandidature->recruitmentcandidature_advance->validate)))) 485 { 486 $this->error='NotEnoughPermissions'; 487 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR); 488 return -1; 489 }*/ 490 491 $now = dol_now(); 492 493 $this->db->begin(); 494 495 // Define new ref 496 if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life 497 { 498 $num = $this->getNextNumRef(); 499 } else { 500 $num = $this->ref; 501 } 502 $this->newref = $num; 503 504 if (!empty($num)) { 505 // Validate 506 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 507 $sql .= " SET ref = '".$this->db->escape($num)."',"; 508 $sql .= " status = ".self::STATUS_VALIDATED; 509 if (!empty($this->fields['date_validation'])) $sql .= ", date_validation = '".$this->db->idate($now)."',"; 510 if (!empty($this->fields['fk_user_valid'])) $sql .= ", fk_user_valid = ".$user->id; 511 $sql .= " WHERE rowid = ".$this->id; 512 513 dol_syslog(get_class($this)."::validate()", LOG_DEBUG); 514 $resql = $this->db->query($sql); 515 if (!$resql) 516 { 517 dol_print_error($this->db); 518 $this->error = $this->db->lasterror(); 519 $error++; 520 } 521 522 if (!$error && !$notrigger) 523 { 524 // Call trigger 525 $result = $this->call_trigger('RECRUITMENTCANDIDATURE_VALIDATE', $user); 526 if ($result < 0) $error++; 527 // End call triggers 528 } 529 } 530 531 if (!$error) 532 { 533 $this->oldref = $this->ref; 534 535 // Rename directory if dir was a temporary ref 536 if (preg_match('/^[\(]?PROV/i', $this->ref)) 537 { 538 // Now we rename also files into index 539 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'recruitmentcandidature/".$this->db->escape($this->newref)."'"; 540 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'recruitmentcandidature/".$this->db->escape($this->ref)."' and entity = ".$conf->entity; 541 $resql = $this->db->query($sql); 542 if (!$resql) { $error++; $this->error = $this->db->lasterror(); } 543 544 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments 545 $oldref = dol_sanitizeFileName($this->ref); 546 $newref = dol_sanitizeFileName($num); 547 $dirsource = $conf->recruitment->dir_output.'/recruitmentcandidature/'.$oldref; 548 $dirdest = $conf->recruitment->dir_output.'/recruitmentcandidature/'.$newref; 549 if (!$error && file_exists($dirsource)) 550 { 551 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest); 552 553 if (@rename($dirsource, $dirdest)) 554 { 555 dol_syslog("Rename ok"); 556 // Rename docs starting with $oldref with $newref 557 $listoffiles = dol_dir_list($conf->recruitment->dir_output.'/recruitmentcandidature/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/')); 558 foreach ($listoffiles as $fileentry) 559 { 560 $dirsource = $fileentry['name']; 561 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource); 562 $dirsource = $fileentry['path'].'/'.$dirsource; 563 $dirdest = $fileentry['path'].'/'.$dirdest; 564 @rename($dirsource, $dirdest); 565 } 566 } 567 } 568 } 569 } 570 571 // Set new ref and current status 572 if (!$error) 573 { 574 $this->ref = $num; 575 $this->status = self::STATUS_VALIDATED; 576 } 577 578 if (!$error) 579 { 580 $this->db->commit(); 581 return 1; 582 } else { 583 $this->db->rollback(); 584 return -1; 585 } 586 } 587 588 589 /** 590 * Set draft status 591 * 592 * @param User $user Object user that modify 593 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 594 * @return int <0 if KO, >0 if OK 595 */ 596 public function setDraft($user, $notrigger = 0) 597 { 598 // Protection 599 if ($this->status <= self::STATUS_DRAFT) 600 { 601 return 0; 602 } 603 604 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->write)) 605 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->recruitment_advance->validate)))) 606 { 607 $this->error='Permission denied'; 608 return -1; 609 }*/ 610 611 return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'RECRUITMENTCANDIDATURE_UNVALIDATE'); 612 } 613 614 /** 615 * Set cancel status 616 * 617 * @param User $user Object user that modify 618 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 619 * @return int <0 if KO, 0=Nothing done, >0 if OK 620 */ 621 public function cancel($user, $notrigger = 0) 622 { 623 // Protection 624 if ($this->status != self::STATUS_VALIDATED) 625 { 626 return 0; 627 } 628 629 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->write)) 630 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->recruitment_advance->validate)))) 631 { 632 $this->error='Permission denied'; 633 return -1; 634 }*/ 635 636 return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'RECRUITMENTCANDIDATURE_CLOSE'); 637 } 638 639 /** 640 * Set back to validated status 641 * 642 * @param User $user Object user that modify 643 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 644 * @return int <0 if KO, 0=Nothing done, >0 if OK 645 */ 646 public function reopen($user, $notrigger = 0) 647 { 648 // Protection 649 if ($this->status != self::STATUS_REFUSED && $this->status != self::STATUS_CANCELED && $this->status != self::STATUS_CONTRACT_REFUSED) 650 { 651 return 0; 652 } 653 654 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->write)) 655 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->recruitment->recruitment_advance->validate)))) 656 { 657 $this->error='Permission denied'; 658 return -1; 659 }*/ 660 661 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'RECRUITMENTCANDIDATURE_REOPEN'); 662 } 663 664 /** 665 * Return a link to the object card (with optionaly the picto) 666 * 667 * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) 668 * @param string $option On what the link point to ('nolink', ...) 669 * @param int $notooltip 1=Disable tooltip 670 * @param string $morecss Add more css on link 671 * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking 672 * @return string String with URL 673 */ 674 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1) 675 { 676 global $conf, $langs, $hookmanager; 677 678 if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips 679 680 $result = ''; 681 682 $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("RecruitmentCandidature").'</u>'; 683 if (isset($this->status)) { 684 $label .= ' '.$this->getLibStatut(5); 685 } 686 $label .= '<br>'; 687 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref; 688 $label .= '<br><b>'.$langs->trans('Email').':</b> '.$this->email; 689 $label .= '<br><b>'.$langs->trans('Name').':</b> '.$this->getFullName($langs); 690 691 $url = dol_buildpath('/recruitment/recruitmentcandidature_card.php', 1).'?id='.$this->id; 692 693 if ($option != 'nolink') 694 { 695 // Add param to save lastsearch_values or not 696 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); 697 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1; 698 if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1'; 699 } 700 701 $linkclose = ''; 702 if (empty($notooltip)) 703 { 704 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) 705 { 706 $label = $langs->trans("ShowRecruitmentCandidature"); 707 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; 708 } 709 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; 710 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"'; 711 } else $linkclose = ($morecss ? ' class="'.$morecss.'"' : ''); 712 713 $linkstart = '<a href="'.$url.'"'; 714 $linkstart .= $linkclose.'>'; 715 $linkend = '</a>'; 716 717 $result .= $linkstart; 718 719 if (empty($this->showphoto_on_popup)) { 720 if ($withpicto) $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); 721 } else { 722 if ($withpicto) { 723 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 724 725 list($class, $module) = explode('@', $this->picto); 726 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref); 727 $filearray = dol_dir_list($upload_dir, "files"); 728 $filename = $filearray[0]['name']; 729 if (!empty($filename)) { 730 $pospoint = strpos($filearray[0]['name'], '.'); 731 732 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint); 733 if (empty($conf->global->{strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS'})) { 734 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$module.'" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div></div>'; 735 } else { 736 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div>'; 737 } 738 739 $result .= '</div>'; 740 } else { 741 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); 742 } 743 } 744 } 745 746 if ($withpicto != 2) $result .= $this->ref; 747 748 $result .= $linkend; 749 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); 750 751 global $action, $hookmanager; 752 $hookmanager->initHooks(array('recruitmentcandidaturedao')); 753 $parameters = array('id'=>$this->id, 'getnomurl'=>$result); 754 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 755 if ($reshook > 0) $result = $hookmanager->resPrint; 756 else $result .= $hookmanager->resPrint; 757 758 return $result; 759 } 760 761 /** 762 * Return label of the status 763 * 764 * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto 765 * @return string Label of status 766 */ 767 public function getLibStatut($mode = 0) 768 { 769 return $this->LibStatut($this->status, $mode); 770 } 771 772 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 773 /** 774 * Return the status 775 * 776 * @param int $status Id status 777 * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto 778 * @return string Label of status 779 */ 780 public function LibStatut($status, $mode = 0) 781 { 782 // phpcs:enable 783 if (empty($this->labelStatus) || empty($this->labelStatusShort)) 784 { 785 global $langs; 786 //$langs->load("recruitment@recruitment"); 787 $this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft'); 788 $this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Received').' ('.$langs->trans("InterviewToDo").')'; 789 $this->labelStatus[self::STATUS_CONTRACT_PROPOSED] = $langs->trans('ContractProposed'); 790 $this->labelStatus[self::STATUS_CONTRACT_SIGNED] = $langs->trans('ContractSigned'); 791 $this->labelStatus[self::STATUS_CONTRACT_REFUSED] = $langs->trans('ContractRefused'); 792 $this->labelStatus[self::STATUS_REFUSED] = $langs->trans('Refused'); 793 $this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Canceled'); 794 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft'); 795 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Received'); 796 $this->labelStatusShort[self::STATUS_CONTRACT_PROPOSED] = $langs->trans('ContractProposed'); 797 $this->labelStatusShort[self::STATUS_CONTRACT_SIGNED] = $langs->trans('ContractSigned'); 798 $this->labelStatusShort[self::STATUS_CONTRACT_REFUSED] = $langs->trans('ContractRefused'); 799 $this->labelStatusShort[self::STATUS_REFUSED] = $langs->trans('Refused'); 800 $this->labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Canceled'); 801 } 802 803 $statusType = 'status'.$status; 804 //if ($status == self::STATUS_VALIDATED) $statusType = 'status1'; 805 if ($status == self::STATUS_CANCELED) $statusType = 'status6'; 806 807 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); 808 } 809 810 /** 811 * Load the info information in the object 812 * 813 * @param int $id Id of object 814 * @return void 815 */ 816 public function info($id) 817 { 818 $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; 819 $sql .= ' fk_user_creat, fk_user_modif'; 820 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; 821 $sql .= ' WHERE t.rowid = '.$id; 822 $result = $this->db->query($sql); 823 if ($result) 824 { 825 if ($this->db->num_rows($result)) 826 { 827 $obj = $this->db->fetch_object($result); 828 $this->id = $obj->rowid; 829 if ($obj->fk_user_author) 830 { 831 $cuser = new User($this->db); 832 $cuser->fetch($obj->fk_user_author); 833 $this->user_creation = $cuser; 834 } 835 836 if ($obj->fk_user_valid) 837 { 838 $vuser = new User($this->db); 839 $vuser->fetch($obj->fk_user_valid); 840 $this->user_validation = $vuser; 841 } 842 843 if ($obj->fk_user_cloture) 844 { 845 $cluser = new User($this->db); 846 $cluser->fetch($obj->fk_user_cloture); 847 $this->user_cloture = $cluser; 848 } 849 850 $this->date_creation = $this->db->jdate($obj->datec); 851 $this->date_modification = $this->db->jdate($obj->datem); 852 $this->date_validation = $this->db->jdate($obj->datev); 853 } 854 855 $this->db->free($result); 856 } else { 857 dol_print_error($this->db); 858 } 859 } 860 861 /** 862 * Initialise object with example values 863 * Id must be 0 if object instance is a specimen 864 * 865 * @return void 866 */ 867 public function initAsSpecimen() 868 { 869 $this->initAsSpecimenCommon(); 870 } 871 872 /** 873 * Create an array of lines 874 * 875 * @return array|int array of lines if OK, <0 if KO 876 */ 877 public function getLinesArray() 878 { 879 $this->lines = array(); 880 881 $objectline = new RecruitmentCandidatureLine($this->db); 882 $result = $objectline->fetchAll('ASC', 'position', 0, 0, array('customsql'=>'fk_recruitmentcandidature = '.$this->id)); 883 884 if (is_numeric($result)) 885 { 886 $this->error = $this->error; 887 $this->errors = $this->errors; 888 return $result; 889 } else { 890 $this->lines = $result; 891 return $this->lines; 892 } 893 } 894 895 /** 896 * Returns the reference to the following non used object depending on the active numbering module. 897 * 898 * @return string Object free reference 899 */ 900 public function getNextNumRef() 901 { 902 global $langs, $conf; 903 $langs->load("recruitment@recruitment"); 904 905 if (empty($conf->global->RECRUITMENT_RECRUITMENTCANDIDATURE_ADDON)) { 906 $conf->global->RECRUITMENT_RECRUITMENTCANDIDATURE_ADDON = 'mod_recruitmentcandidature_standard'; 907 } 908 909 if (!empty($conf->global->RECRUITMENT_RECRUITMENTCANDIDATURE_ADDON)) 910 { 911 $mybool = false; 912 913 $file = $conf->global->RECRUITMENT_RECRUITMENTCANDIDATURE_ADDON.".php"; 914 $classname = $conf->global->RECRUITMENT_RECRUITMENTCANDIDATURE_ADDON; 915 916 // Include file with class 917 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); 918 foreach ($dirmodels as $reldir) 919 { 920 $dir = dol_buildpath($reldir."core/modules/recruitment/"); 921 922 // Load file with numbering class (if found) 923 $mybool |= @include_once $dir.$file; 924 } 925 926 if ($mybool === false) 927 { 928 dol_print_error('', "Failed to include file ".$file); 929 return ''; 930 } 931 932 if (class_exists($classname)) { 933 $obj = new $classname(); 934 $numref = $obj->getNextValue($this); 935 936 if ($numref != '' && $numref != '-1') 937 { 938 return $numref; 939 } else { 940 $this->error = $obj->error; 941 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); 942 return ""; 943 } 944 } else { 945 print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname; 946 return ""; 947 } 948 } else { 949 print $langs->trans("ErrorNumberingModuleNotSetup", $this->element); 950 return ""; 951 } 952 } 953 954 /** 955 * Create a document onto disk according to template module. 956 * 957 * @param string $modele Force template to use ('' to not force) 958 * @param Translate $outputlangs objet lang a utiliser pour traduction 959 * @param int $hidedetails Hide details of lines 960 * @param int $hidedesc Hide description 961 * @param int $hideref Hide ref 962 * @param null|array $moreparams Array to provide more information 963 * @return int 0 if KO, 1 if OK 964 */ 965 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) 966 { 967 global $conf, $langs; 968 969 $result = 0; 970 $includedocgeneration = 0; 971 972 $langs->load("recruitment@recruitment"); 973 974 if (!dol_strlen($modele)) { 975 if (!empty($conf->global->RECRUITMENTCANDIDATURE_ADDON_PDF)) 976 { 977 $modele = $conf->global->RECRUITMENTCANDIDATURE_ADDON_PDF; 978 } else { 979 $modele = ''; // No default value. For job application, we allow to disable all PDF generation 980 } 981 } 982 983 $modelpath = "core/modules/recruitment/doc/"; 984 985 if ($includedocgeneration && !empty($modele)) { 986 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); 987 } 988 989 return $result; 990 } 991 992 /** 993 * Action executed by scheduler 994 * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters' 995 * Use public function doScheduledJob($param1, $param2, ...) to get parameters 996 * 997 * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) 998 */ 999 public function doScheduledJob() 1000 { 1001 global $conf, $langs; 1002 1003 //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log'; 1004 1005 $error = 0; 1006 $this->output = ''; 1007 $this->error = ''; 1008 1009 dol_syslog(__METHOD__, LOG_DEBUG); 1010 1011 $now = dol_now(); 1012 1013 $this->db->begin(); 1014 1015 // ... 1016 1017 $this->db->commit(); 1018 1019 return $error; 1020 } 1021} 1022 1023 1024require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php'; 1025 1026/** 1027 * Class RecruitmentCandidatureLine. You can also remove this and generate a CRUD class for lines objects. 1028 */ 1029class RecruitmentCandidatureLine extends CommonObjectLine 1030{ 1031 // To complete with content of an object RecruitmentCandidatureLine 1032 // We should have a field rowid, fk_recruitmentcandidature and position 1033 1034 /** 1035 * Constructor 1036 * 1037 * @param DoliDb $db Database handler 1038 */ 1039 public function __construct(DoliDB $db) 1040 { 1041 $this->db = $db; 1042 } 1043} 1044