TTi18n::gettext( 'UNREAD' ), 20 => TTi18n::gettext( 'READ' ), ]; break; case 'type': $retval = [ 5 => 'email', //10 => 'default_schedule', //20 => 'schedule_amendment', //30 => 'shift_amendment', 40 => 'authorization', 50 => 'request', 60 => 'job', 70 => 'job_item', 80 => 'client', 90 => 'timesheet', 100 => 'user' //For notes assigned to users? ]; break; case 'type_to_api_map': //Maps the object_type_id to an API class that we can use to determine if the user has access to view the specific records or not. $retval = [ //5 => 'email', //Email is never linked to another class //10 => 'default_schedule', //20 => 'schedule_amendment', //30 => 'shift_amendment', 40 => 'APIAuthorization', 50 => 'APIRequest', 60 => 'APIJob', 70 => 'APIJobItem', 80 => 'APIClient', 90 => 'APITimeSheet', 100 => 'APIUser' //For notes assigned to users? ]; break; case 'object_type': case 'object_name': $retval = [ 5 => TTi18n::gettext( 'Email' ), //Email from user to another 10 => TTi18n::gettext( 'Recurring Schedule' ), 20 => TTi18n::gettext( 'Schedule Amendment' ), 30 => TTi18n::gettext( 'Shift Amendment' ), 40 => TTi18n::gettext( 'Authorization' ), 50 => TTi18n::gettext( 'Request' ), 60 => TTi18n::gettext( 'Job' ), 70 => TTi18n::gettext( 'Task' ), 80 => TTi18n::gettext( 'Client' ), 90 => TTi18n::gettext( 'TimeSheet' ), 100 => TTi18n::gettext( 'Employee' ) //For notes assigned to users? ]; break; case 'folder': $retval = [ 10 => TTi18n::gettext( 'Inbox' ), 20 => TTi18n::gettext( 'Sent' ), ]; break; case 'priority': $retval = [ 10 => TTi18n::gettext( 'LOW' ), 50 => TTi18n::gettext( 'NORMAL' ), 100 => TTi18n::gettext( 'HIGH' ), 110 => TTi18n::gettext( 'URGENT' ), ]; break; case 'columns': $retval = [ '-1010-from_first_name' => TTi18n::gettext( 'From: First Name' ), '-1020-from_middle_name' => TTi18n::gettext( 'From: Middle Name' ), '-1030-from_last_name' => TTi18n::gettext( 'From: Last Name' ), '-1110-to_first_name' => TTi18n::gettext( 'To: First Name' ), '-1120-to_middle_name' => TTi18n::gettext( 'To: Middle Name' ), '-1130-to_last_name' => TTi18n::gettext( 'To: Last Name' ), '-1200-subject' => TTi18n::gettext( 'Subject' ), '-1210-object_type' => TTi18n::gettext( 'Type' ), '-2000-created_by' => TTi18n::gettext( 'Created By' ), '-2010-created_date' => TTi18n::gettext( 'Created Date' ), //'-2020-updated_by' => TTi18n::gettext('Updated By'), //'-2030-updated_date' => TTi18n::gettext('Updated Date'), ]; break; case 'list_columns': $retval = Misc::arrayIntersectByKey( $this->getOptions( 'default_display_columns' ), Misc::trimSortPrefix( $this->getOptions( 'columns' ) ) ); break; case 'default_display_columns': //Columns that are displayed by default. $retval = [ 'from_first_name', 'from_last_name', 'to_first_name', 'to_last_name', 'subject', 'object_type', 'created_date', ]; break; } return $retval; } /** * @param $data * @return array */ function _getVariableToFunctionMap( $data ) { $variable_function_map = [ 'id' => 'ID', 'from_user_id' => 'FromUserID', 'from_first_name' => false, 'from_middle_name' => false, 'from_last_name' => false, 'to_user_id' => 'ToUserID', 'to_first_name' => false, 'to_middle_name' => false, 'to_last_name' => false, 'status_id' => false, 'object_type_id' => 'ObjectType', 'object_type' => false, 'object_id' => 'Object', 'parent_id' => 'Parent', 'priority_id' => 'Priority', 'subject' => 'Subject', 'body' => 'Body', 'require_ack' => 'RequireAck', 'deleted' => 'Deleted', ]; return $variable_function_map; } /** * @return bool */ function getFromUserObject() { return $this->getGenericObject( 'UserListFactory', $this->getFromUserID(), 'from_user_obj' ); } /** * @return bool */ function getFromUserId() { return $this->getGenericTempDataValue( 'from_user_id' ); } /** * @param string $value UUID * @return bool */ function setFromUserId( $value ) { if ( $value != '' ) { return $this->setGenericTempDataValue( 'from_user_id', $value ); } return false; } /** * @return bool|array|string */ function getToUserId() { return $this->getGenericTempDataValue( 'to_user_id' ); } /** * @param string|string[] $ids UUID * @return bool */ function setToUserId( $ids ) { if ( !is_array( $ids ) ) { $ids = [ $ids ]; } $ids = array_unique( $ids ); if ( count( $ids ) > 0 ) { $tmp_ids = []; //Reset the TO array, so if this is called multiple times, we don't keep adding more and more users to it. foreach ( $ids as $id ) { if ( TTUUID::isUUID( $id ) && $id != TTUUID::getZeroID() && $id != TTUUID::getNotExistID() ) { $tmp_ids[] = $id; } } $this->setGenericTempDataValue( 'to_user_id', $tmp_ids ); return true; } return false; } /** * Expose message_sender_id for migration purposes. * @return bool */ function getMessageSenderId() { return $this->getGenericTempDataValue( 'message_sender_id' ); } /** * @param string $value UUID * @return bool */ function setMessageSenderId( $value ) { if ( $value != '' ) { return $this->setGenericTempDataValue( 'message_sender_id', $value ); } return false; } /** * @return bool */ function isAck() { if ( $this->getRequireAck() == true && $this->getColumn( 'ack_date' ) == '' ) { return false; } return true; } /** * Parent ID is the parent message_sender_id. * @return bool */ function getParent() { return $this->getGenericTempDataValue( 'parent_id' ); } /** * @param string $value UUID * @return bool */ function setParent( $value ) { $value = TTUUID::castUUID( $value ); return $this->setGenericTempDataValue( 'parent_id', $value ); } /** * These functions are out of the ordinary, as the getStatus gets the status of a message based on a SQL join to the recipient table. * @return bool|int */ function getStatus() { return (int)$this->getGenericDataValue( 'status_id' ); } /** * @return null|object */ function getObjectHandler() { if ( is_object( $this->obj_handler ) ) { return $this->obj_handler; } else { switch ( $this->getObjectType() ) { case 5: case 100: $this->obj_handler = TTnew( 'UserListFactory' ); break; case 40: $this->obj_handler = TTnew( 'AuthorizationListFactory' ); break; case 50: $this->obj_handler = TTnew( 'RequestListFactory' ); break; case 90: $this->obj_handler = TTnew( 'PayPeriodTimeSheetVerifyListFactory' ); break; } return $this->obj_handler; } } /** * @return bool|int */ function getObjectType() { return (int)$this->getGenericDataValue( 'object_type_id' ); } /** * @param $value * @return bool */ function setObjectType( $value ) { $value = trim( $value ); return $this->setGenericDataValue( 'object_type_id', $value ); } /** * @return bool|mixed */ function getObject() { return $this->getGenericDataValue( 'object_id' ); } /** * @param string $value UUID * @return bool */ function setObject( $value ) { $value = trim( $value ); return $this->setGenericDataValue( 'object_id', $value ); } /** * @return bool|int */ function getPriority() { return $this->getGenericDataValue( 'priority_id' ); } /** * @param null $value * @return bool */ function setPriority( $value = null ) { $value = (int)trim( $value ); if ( empty( $value ) ) { $value = 50; } return $this->setGenericDataValue( 'priority_id', $value ); } /** * @return bool|mixed */ function getSubject() { return $this->getGenericDataValue( 'subject' ); } /** * @param $value * @return bool */ function setSubject( $value ) { $value = trim( $value ); return $this->setGenericDataValue( 'subject', $value ); } /** * @return bool|mixed */ function getBody() { return $this->getGenericDataValue( 'body' ); } /** * @param $value * @return bool */ function setBody( $value ) { $value = trim( $value ); return $this->setGenericDataValue( 'body', $value ); } /** * @return bool */ function getRequireAck() { return $this->fromBool( $this->getGenericDataValue( 'require_ack' ) ); } /** * @param $value * @return bool */ function setRequireAck( $value ) { return $this->setGenericDataValue( 'require_ack', $this->toBool( $value ) ); } /** * @return bool */ function getEnableNotificationMessage() { if ( isset( $this->send_notification ) ) { return $this->send_notification; } return true; } /** * @param $bool * @return bool */ function setEnableNotificationMessage( $bool ) { $this->send_notification = $bool; return true; } /** * @return bool */ function sendNotification() { Debug::Text( 'Sending notification...', __FILE__, __LINE__, __METHOD__, 10 ); //Remove the From User from any recipicient list so we don't notify ourselves. $user_ids = array_diff( $this->getToUserId(), [ $this->getFromUserId() ] ); if ( $user_ids == false ) { return false; } //Get from User Object so we can include more information in the message. if ( is_object( $this->getFromUserObject() ) ) { $u_obj = $this->getFromUserObject(); } else { Debug::Text( 'From object does not exist: ' . $this->getFromUserID(), __FILE__, __LINE__, __METHOD__, 10 ); return false; } foreach ( $user_ids as $user_id ) { //Grab each users preferences and etc can be custom to them and their language etc. $ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */ $ulf->getById( $user_id ); if ( $ulf->getRecordCount() == 1 ) { $user_to_obj = $ulf->getCurrent(); if ( is_object( $user_to_obj ) ) { $user_to_pref_obj = $user_to_obj->getUserPreferenceObject(); /** @var UserPreferenceFactory $user_to_pref_obj */ $user_to_pref_obj->setDateTimePreferences(); TTi18n::setLanguage( $user_to_pref_obj->getLanguage() ); TTi18n::setCountry( $user_to_obj->getCountry() ); TTi18n::setLocale(); } else { return false; } } //If its a message linked to a request, send them to the request if ( $this->getObjectType() == 50 ) { $object_id = $this->getObject(); $view_name = 'Request'; $action_name = 'view'; $direct_link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=Request&a=view&id=' . $object_id . '&tab=Request'; } else { //Message $object_id = $this->getMessageSenderId(); $view_name = 'Message'; $action_name = 'view'; $direct_link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=MessageControl&a=view&id=' . $object_id . '&tab=Message'; } $payload = [ 'timetrex' => [ 'event' => [ [ 'type' => 'open_view', 'data' => [ 'id' => $object_id ], 'view_name' => $view_name, 'action_name' => $action_name ] ] ], 'link' => $direct_link ]; //Open Message or Request view. //Define subject/body variables here. $search_arr = [ '#from_employee_first_name#', '#from_employee_last_name#', '#from_employee_default_branch#', '#from_employee_default_department#', '#from_employee_group#', '#from_employee_title#', '#company_name#', '#url#', ]; $replace_arr = Misc::escapeHTML( [ $u_obj->getFirstName(), $u_obj->getLastName(), ( is_object( $u_obj->getDefaultBranchObject() ) ) ? $u_obj->getDefaultBranchObject()->getName() : null, ( is_object( $u_obj->getDefaultDepartmentObject() ) ) ? $u_obj->getDefaultDepartmentObject()->getName() : null, ( is_object( $u_obj->getGroupObject() ) ) ? $u_obj->getGroupObject()->getName() : null, ( is_object( $u_obj->getTitleObject() ) ) ? $u_obj->getTitleObject()->getName() : null, ( is_object( $u_obj->getCompanyObject() ) ) ? $u_obj->getCompanyObject()->getName() : null, $direct_link, ] ); $subject = TTi18n::gettext( 'New message from' ) . ' ' . $u_obj->getFullName(); //$email_body = TTi18n::gettext( '*DO NOT REPLY TO THIS EMAIL - PLEASE USE THE LINK BELOW INSTEAD*' ) . "\n\n"; $email_body = TTi18n::gettext( 'You have a new message waiting for you in' ) . ' ' . APPLICATION_NAME . ".\n\n"; $email_body .= TTi18n::gettext( 'From' ) . ': #from_employee_first_name# #from_employee_last_name#' . "\n"; $email_body .= ( $this->getSubject() != '' ) ? TTi18n::gettext( 'Subject' ) . ': ' . Misc::escapeHTML( $this->getSubject() ) . "\n" : null; $email_body .= ( $replace_arr[2] != '' ) ? TTi18n::gettext( 'Default Branch' ) . ': #from_employee_default_branch#' . "\n" : null; $email_body .= ( $replace_arr[3] != '' ) ? TTi18n::gettext( 'Default Department' ) . ': #from_employee_default_department#' . "\n" : null; $email_body .= ( $replace_arr[4] != '' ) ? TTi18n::gettext( 'Group' ) . ': #from_employee_group#' . "\n" : null; $email_body .= ( $replace_arr[5] != '' ) ? TTi18n::gettext( 'Title' ) . ': #from_employee_title#' . "\n" : null; $email_body .= TTi18n::gettext( 'Link' ) . ': ' . APPLICATION_NAME . ' ' . TTi18n::gettext( 'Login' ) . '' . "\n"; $email_body .= NotificationFactory::addEmailFooter( ( ( is_object( $u_obj->getCompanyObject() ) ) ? $u_obj->getCompanyObject()->getName() : null ) ); $email_body = '
' . str_replace( $search_arr, $replace_arr, $email_body ) . ''; $notification_data = [ 'object_id' => $this->getId(), 'user_id' => $user_id, 'type_id' => 'message', 'object_type_id' => 80, 'title_short' => $subject, 'title_long' => $subject, 'body_short' => $this->getSubject(), //The email subject is the notification body, the recipient must view the entire email to see the body. 'body_long_html' => $email_body, 'payload' => $payload, ]; Notification::sendNotification( $notification_data ); } //reset datetime and tti8n preferences to current user $user_pref_obj = $u_obj->getUserPreferenceObject(); /** @var UserPreferenceFactory $user_pref_obj */ $user_pref_obj->setDateTimePreferences(); TTi18n::setLanguage( $user_pref_obj->getLanguage() ); TTi18n::setCountry( $u_obj->getCountry() ); TTi18n::setLocale(); return true; } /** * @param bool $ignore_warning * @return bool */ function Validate( $ignore_warning = true ) { // // BELOW: Validation code moved from set*() functions. // // Parent if ( $this->getParent() !== false && $this->getParent() != TTUUID::getZeroID() ) { $this->Validator->isUUID( 'parent', $this->getParent(), TTi18n::gettext( 'Parent is invalid' ) ); } // Object Type $this->Validator->inArrayKey( 'object_type', $this->getObjectType(), TTi18n::gettext( 'Object Type is invalid' ), $this->getOptions( 'type' ) ); // Object $this->Validator->isResultSetWithRows( 'object', ( is_object( $this->getObjectHandler() ) ) ? $this->getObjectHandler()->getByID( $this->getObject() ) : false, TTi18n::gettext( 'Object is invalid' ) ); // Priority if ( $this->getPriority() !== false ) { $this->Validator->inArrayKey( 'priority', $this->getPriority(), TTi18n::gettext( 'Invalid Priority' ), $this->getOptions( 'priority' ) ); } // Subject if ( $this->getSubject() !== false ) { $this->Validator->isLength( 'subject', $this->getSubject(), TTi18n::gettext( 'Subject is too short' ), 2, 99999 ); $this->Validator->isLength( 'subject', $this->getSubject(), TTi18n::gettext( 'Subject is too long' ), 0, 100 ); } // Message body //Flex interface validates the message too soon, make it skip a 0 length message when only validating. if ( $this->Validator->getValidateOnly() == true && $this->getBody() == '' ) { $minimum_length = 0; } else { $minimum_length = 2; } $this->Validator->isLength( 'body', $this->getBody(), TTi18n::gettext( 'Message body is too short.' ), $minimum_length, ( 1024 * 9999999 ) ); if ( $this->Validator->isError( 'body' ) == false ) { $this->Validator->isLength( 'body', $this->getBody(), TTi18n::gettext( 'Message body is too long.' ), 0, ( 1024 * 10 ) ); } // // ABOVE: Validation code moved from set*() functions. // //Only validate from/to user if there is a subject and body set, otherwise validation will fail on a new object with no data all the time. if ( $this->getSubject() != '' && $this->getBody() != '' ) { if ( $this->Validator->hasError( 'from' ) == false && $this->getFromUserId() == '' ) { $this->Validator->isTrue( 'from', false, TTi18n::gettext( 'Message sender is invalid' ) ); } //Messages attached to objects do not require a recipient. if ( $this->Validator->hasError( 'to' ) == false && $this->getObjectType() == 5 && ( $this->getToUserId() == '' || ( is_array( $this->getToUserId() ) && count( $this->getToUserId() ) == 0 ) ) ) { $this->Validator->isTrue( 'to_user_id', false, TTi18n::gettext( 'Please specify at least one employee' ) ); } } if ( $this->Validator->getValidateOnly() == false ) { if ( $this->getObjectType() == '' ) { $this->Validator->isTrue( 'object_type_id', false, TTi18n::gettext( 'Object type is invalid' ) ); } if ( $this->Validator->hasError( 'object' ) == false && $this->getObject() == '' ) { $this->Validator->isTrue( 'object', false, TTi18n::gettext( 'Object must be specified' ) ); } } //If deleted is TRUE, we need to make sure all sender/recipient records are also deleted. return true; } /** * @param string $company_id UUID * @param string $user_id UUID * @param string|array $ids UUID * @return bool */ static function markRecipientMessageAsRead( $company_id, $user_id, $ids ) { if ( $company_id == '' || $user_id == '' || $ids == '' || ( is_array( $ids ) && count( $ids ) == 0 ) ) { return false; } Debug::Arr( $ids, 'Message Recipeint Ids: ', __FILE__, __LINE__, __METHOD__, 10 ); $mrlf = TTnew( 'MessageRecipientListFactory' ); /** @var MessageRecipientListFactory $mrlf */ $mrlf->getByCompanyIdAndUserIdAndMessageSenderIdAndStatus( $company_id, $user_id, $ids, 10 ); if ( $mrlf->getRecordCount() > 0 ) { foreach ( $mrlf as $mr_obj ) { $mr_obj->setStatus( 20 ); //Read if ( $mr_obj->isValid() ) { $mr_obj->Save( false ); //Keep object so we can use it below. if ( TTUUID::isUUID( $mr_obj->getColumn('message_control_id') ) ) { NotificationFactory::updateStatusByObjectIdAndObjectTypeId( 80, $mr_obj->getColumn('message_control_id') ); //80=Message, Mark any notifications linked to these exceptions as read. } } } } return true; } /** * @return bool */ function preSave() { //Check to make sure the 'From' user_id doesn't appear in the 'To' user list as well. $from_user_id_key = array_search( $this->getFromUserId(), (array)$this->getToUserId() ); if ( $from_user_id_key !== false ) { $to_user_ids = $this->getToUserId(); unset( $to_user_ids[$from_user_id_key] ); $this->setToUserId( $to_user_ids ); Debug::text( 'From user is assigned as a To user as well, removing...' . $from_user_id_key, __FILE__, __LINE__, __METHOD__, 9 ); } Debug::Arr( $this->getFromUserId(), 'From: ', __FILE__, __LINE__, __METHOD__, 9 ); Debug::Arr( $this->getToUserId(), 'Sending To: ', __FILE__, __LINE__, __METHOD__, 9 ); return true; } /** * @return bool */ function postSave() { //Save Sender/Recipient records for this message. if ( $this->getDeleted() == false ) { $to_user_ids = $this->getToUserId(); if ( $to_user_ids != false ) { foreach ( $to_user_ids as $to_user_id ) { //We need one message_sender record for every recipient record, otherwise when a message is sent to //multiple recipients, and one of them replies, the parent_id will point to original sender record which //then maps to every single recipient, making it hard to show messages just between the specific users. // //On the other hand, having multiple sender records, one for each recipient makes it hard to show //just the necessary messages on the embedded message list, as it wants to show duplicates messages for //each recipient. $msf = TTnew( 'MessageSenderFactory' ); /** @var MessageSenderFactory $msf */ $msf->setUser( $this->getFromUserId() ); Debug::Text( 'Parent ID: ' . $this->getParent(), __FILE__, __LINE__, __METHOD__, 10 ); //Only specify parent if the object type is message. if ( $this->getObjectType() == 5 ) { $msf->setParent( $this->getParent() ); } else { $msf->setParent( TTUUID::getZeroID() ); } $msf->setMessageControl( $this->getId() ); $msf->setCreatedBy( $this->getCreatedBy() ); $msf->setCreatedDate( $this->getCreatedDate() ); $msf->setUpdatedBy( $this->getUpdatedBy() ); $msf->setUpdatedDate( $this->getUpdatedDate() ); if ( $msf->isValid() ) { $message_sender_id = $msf->Save(); $this->setMessageSenderId( $message_sender_id ); //Used mainly for migration purposes, so we can obtain this from outside the class. Debug::Text( 'Message Sender ID: ' . $message_sender_id, __FILE__, __LINE__, __METHOD__, 10 ); if ( $message_sender_id != false ) { $mrf = TTnew( 'MessageRecipientFactory' ); /** @var MessageRecipientFactory $mrf */ $mrf->setUser( $to_user_id ); $mrf->setMessageSender( $message_sender_id ); if ( isset( $this->migration_status ) ) { $mrf->setStatus( $this->migration_status ); } $mrf->setCreatedBy( $this->getCreatedBy() ); $mrf->setCreatedDate( $this->getCreatedDate() ); $mrf->setUpdatedBy( $this->getUpdatedBy() ); $mrf->setUpdatedDate( $this->getUpdatedDate() ); if ( $mrf->isValid() ) { $mrf->Save(); } } } } //Send notification to all recipients. if ( $this->getEnableNotificationMessage() == true ) { $this->sendNotification(); } } } return true; } /** * @param $data * @return bool */ function setObjectFromArray( $data ) { if ( is_array( $data ) ) { $variable_function_map = $this->getVariableToFunctionMap(); foreach ( $variable_function_map as $key => $function ) { if ( isset( $data[$key] ) ) { $function = 'set' . $function; switch ( $key ) { default: if ( method_exists( $this, $function ) ) { $this->$function( $data[$key] ); } break; } } } $this->setCreatedAndUpdatedColumns( $data ); return true; } return false; } /** * @param null $include_columns * @return array */ function getObjectAsArray( $include_columns = null ) { $variable_function_map = $this->getVariableToFunctionMap(); $data = []; if ( is_array( $variable_function_map ) ) { foreach ( $variable_function_map as $variable => $function_stub ) { if ( $include_columns == null || ( isset( $include_columns[$variable] ) && $include_columns[$variable] == true ) ) { $function = 'get' . $function_stub; switch ( $variable ) { case 'to_user_id': case 'to_first_name': case 'to_middle_name': case 'to_last_name': case 'from_user_id': case 'from_first_name': case 'from_middle_name': case 'from_last_name': $data[$variable] = $this->getColumn( $variable ); break; case 'status_id': $data[$variable] = $this->getStatus(); //Make sure this is returned as an INT. break; case 'object_type': $data[$variable] = Option::getByKey( $this->getObjectType(), $this->getOptions( $variable ) ); break; default: if ( method_exists( $this, $function ) ) { $data[$variable] = $this->$function(); } break; } } } $this->getCreatedAndUpdatedColumns( $data, $include_columns ); } return $data; } } ?>