<?php
/*
 * Copyright (c) 2025, Tribal Limited
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Zenario, Tribal Limited nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TRIBAL LTD BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
if (!defined('NOT_ACCESSED_DIRECTLY')) exit('This file may not be directly accessed');

				
class zenario_users__admin_boxes__site_settings extends ze\moduleBaseClass {

	public function fillAdminBox($path, $settingGroup, &$box, &$fields, &$values){
		switch ($settingGroup) {
			case 'users':
				$groups = ze\datasetAdm::listCustomFields('users', $flat = false, 'boolean_and_groups_only', $customOnly = true, $useOptGroups = true);
				$box['tabs']['inactive_user_email']['fields']['user_dataset_field_to_receive_emails']['values'] = $groups;
		
				// Disable setting for deleting unconfirmed user accounts if scheduled task manager is not enabled
				$scheduledTaskManagerRunning = ze\row::exists('modules', ['class_name' => 'zenario_scheduled_task_manager', 'status' => 'module_running']);
				if (!$scheduledTaskManagerRunning 
					|| !ze::setting('site_enabled')
					|| !ze::setting('jobs_enabled') 
					|| !ze\module::inc('zenario_scheduled_task_manager')
					|| !zenario_scheduled_task_manager::checkScheduledTaskRunning('jobRemoveInactivePendingUsers')
				) {
					$values['unconfirmed_users/remove_inactive_users'] = false;
					$fields['unconfirmed_users/remove_inactive_users']['disabled'] = true;
					
					$fields['unconfirmed_users/remove_inactive_users']['side_note'] = 
						ze\admin::phrase('The scheduled task manager module must be running and the task "jobRemoveInactivePendingUsers" must be enabled to remove inactive users.');
				}
				
				if (!$scheduledTaskManagerRunning 
					|| !ze::setting('site_enabled')
					|| !ze::setting('jobs_enabled') 
					|| !ze\module::inc('zenario_scheduled_task_manager')
					|| !zenario_scheduled_task_manager::checkScheduledTaskRunning('jobSendInactiveUserEmail')
				) {
					$values['inactive_user_email/time_user_inactive_1'] = 
					$values['inactive_user_email/time_user_inactive_2'] = 
					$values['inactive_user_email/user_dataset_field_to_receive_emails'] = false;
					
					$fields['inactive_user_email/time_user_inactive_1']['disabled'] = 
					$fields['inactive_user_email/time_user_inactive_2']['disabled'] = 
					$fields['inactive_user_email/user_dataset_field_to_receive_emails']['disabled'] = true;
					
					$fields['inactive_user_email/time_user_inactive_1']['side_note'] = 
					$fields['inactive_user_email/time_user_inactive_2']['side_note'] = 
					$fields['inactive_user_email/user_dataset_field_to_receive_emails']['side_note'] = 
						ze\admin::phrase('The scheduled task manager module must be running and the task "jobSendInactiveUserEmail" must be enabled to remove inactive users.');
				}
		
				// Show list of users dataset tabs.
				// Please note: as of Zenario 9.4, task T12219, "Users and Contacts settings box, should not allow Groups tab to be on Dates",
				// do not show the "Dates/History" tab.
				$dataset = ze\dataset::details('users');
				$result = ze\row::query('custom_dataset_tabs', ['label', 'default_label', 'ord', 'name', 'is_system_field'], ['dataset_id' => $dataset['id'], 'name' => ['!' => 'dates']], 'label');
				while ($row = ze\sql::fetchAssoc($result)) {
					$label = $row['label'] ? $row['label'] : $row['default_label'];
					if ($label !== false && $label !== '') {
						$label .= ' (';
						
						if ($row['is_system_field']) {
							$label .= ze\admin::phrase('system tab');
						} else {
							$label .= ze\admin::phrase('custom tab');
						}
						
						$label .= ', ' . ze\admin::phrase("code") . ' "' . $row['name'] . '")';
						
						$fields['groups/default_groups_dataset_tab']['values'][$row['name']] = [
							'label' => $label,
							'ord' => $row['ord']
						];
						
						$fields['flags/default_flags_dataset_tab']['values'][$row['name']] = [
							'label' => $label,
							'ord' => $row['ord']
						];
					}
				}
				
				$link = ze\link::absolute() . '/organizer.php#zenario__administration/panels/site_settings//data_protection~.site_settings~tdata_protection~k{"id"%3A"data_protection"}';
				$fields['names/data_protection_link']['snippet']['html'] = ze\admin::phrase('See the <a target="_blank" href="[[link]]">data protection</a> panel for settings on how long to keep user sign-in and content-access logs.', ['link' => htmlspecialchars($link)]);
				
				//Information about max upload sizes
				if (isset($fields['groups/global_upload_setting_info'])) {
					$apacheMaxFilesize = ze\dbAdm::apacheMaxFilesize();
				
					$zenarioMaxFilesizeValue = ze::setting('content_max_filesize');
					$zenarioMaxFilesizeUnit = ze::setting('content_max_filesize_unit');
					$zenarioMaxFilesize = ze\file::fileSizeBasedOnUnit($zenarioMaxFilesizeValue, $zenarioMaxFilesizeUnit);
	
					$maxFileSize = min($apacheMaxFilesize, $zenarioMaxFilesize);
					$maxFileSizeFormatted = ze\file::fileSizeConvert($maxFileSize);
				
					$globalUploadSettingString = ze\admin::phrase('File size will always be limited by global file upload size setting ([[maxFileSizeFormatted]]).', ['maxFileSizeFormatted' => $maxFileSizeFormatted]);
					ze\lang::applyMergeFields(
						$fields['groups/global_upload_setting_info']['snippet']['html'],
						['global_upload_setting_string' => $globalUploadSettingString]
					);
				}
				
				if (ze\module::isRunning('zenario_extranet')) {
					$fields['activation_email_template/extranet_not_running']['hidden'] = true;
				} else {
					$fields['activation_email_template/extranet_not_running']['hidden'] = false;
					
					$fields['activation_email_template/default_creation_email_template']['hidden'] =
					$fields['activation_email_template/default_activation_email_template']['hidden'] =
					$fields['activation_email_template/default_verification_email_template']['hidden'] =
					$fields['activation_email_template/default_password_reset_email_template']['hidden'] =
					$fields['activation_email_template/verification_email_expiry_period']['hidden'] = true;
					
					unset(
						$fields['activation_email_template/default_creation_email_template']['value'],
						$fields['activation_email_template/default_activation_email_template']['value'],
						$fields['activation_email_template/default_verification_email_template']['value'],
						$fields['activation_email_template/default_password_reset_email_template']['value']
					);
				}
				
				$box['key']['current_password_min_score_value'] = $values['passwords/min_extranet_user_password_score'];
				
				break;
			
			case 'perms':
				//Check to see what's running
				$ZENARIO_ORGANIZATION_MANAGER_PREFIX = ze\module::prefix('zenario_organization_manager', true);
				$ZENARIO_COMPANY_LOCATIONS_MANAGER_PREFIX = ze\module::prefix('zenario_company_locations_manager', true);
				
				if ($ZENARIO_ORGANIZATION_MANAGER_PREFIX) {
					$box['lovs']['roles'] = ze\row::getValues($ZENARIO_ORGANIZATION_MANAGER_PREFIX. 'user_location_roles', 'name', [], 'name', 'id');
				}
				
				
				//If the dev tools are on, add an example as a site note to each type of permissions check
				if (ze\admin::setting('show_dev_tools')) {
					foreach ($box['tabs'] as &$tab) {
						if (empty($tab['hidden']) && !empty($tab['fields']) && is_array($tab['fields'])) {
							foreach ($tab['fields'] as $fieldId => &$field) {
								if (!empty($field) && is_array($field)) {
									if ($fieldId) {
										
										$atLocation = stripos($fieldId, 'atLocation') !== false;
										$atCompany = stripos($fieldId, 'atCompany') !== false;
										
										//Don't show company/location related permissions if the modules are not running
										if ((!$ZENARIO_ORGANIZATION_MANAGER_PREFIX && ($atCompany || $atLocation))
										 || (!$ZENARIO_COMPANY_LOCATIONS_MANAGER_PREFIX && $atCompany)) {
											$field['hidden'] = true;
										
										} else {
											$directlyAssignedToUser =
											$byGroupAndCountry =
											$hasRoleAtCompany =
											$hasRoleAtLocation =
											$hasRoleAtLocationAtCompany =
											$onlyIfHasRolesAtAllAssignedLocations = false;
											
											//Check if this field is one of the permissions
											if (($permName = ze\ring::chopPrefix('perm.', $fieldId))
											 && (ze\user::checkNamedPermExists($permName, $directlyAssignedToUser, $byGroupAndCountry, $hasRoleAtCompany, $hasRoleAtLocation, $hasRoleAtLocationAtCompany, $onlyIfHasRolesAtAllAssignedLocations))) {
												
												//Break the permission code-name up into chunks
												$perm = explode('.', $fieldId);
												
												//Show the ze\user::canCreate() shortcut function if this is a create permission
												if ($thingToCreate = ze\ring::chopPrefix('create-', $perm[1])) {
													$field['side_note'] = '<code>ze\\user::canCreate(\''. htmlspecialchars($thingToCreate). '\'';
												} else {
													$field['side_note'] = '<code>ze\\user::can(\''. htmlspecialchars($perm[1]). '\'';
												}
												
												//Companies and locations always use the "unassigned" option, so don't bother showing it
												if ($fieldId != 'perm.create-company.unassigned'
												 && $fieldId != 'perm.create-location.unassigned') {
													
													//Show the target
													$field['side_note'] .= ', \''. htmlspecialchars($perm[2]). '\'';
													
													//Except for unassigned/oneself, show a number as an example for the third input
													if ($perm[2] != 'unassigned' && $perm[2] != 'oneself') {
														$field['side_note'] .= ', 123';
													}
												}
												$field['side_note'] .= ');</code>';
											}
										}
									}
								}
							}
						}
					}
				}
				
				break;
			case 'data_protection':
				//Show a table of users dataset fields and whether they are encrypted or not
				$dataset = ze\dataset::details('users');
				$sql = '
					SELECT cdf.id, cdf.db_column, cdf.is_system_field, cdf.type
					FROM ' . DB_PREFIX . 'custom_dataset_fields cdf
					INNER JOIN ' . DB_PREFIX . 'custom_dataset_tabs cdt
						ON cdf.tab_name = cdt.name
						AND cdf.dataset_id = cdt.dataset_id
					WHERE cdf.dataset_id = ' . (int)$dataset['id'] . '
					AND db_column != ""
					ORDER BY cdt.ord, cdf.ord';
				$result = ze\sql::select($sql);
				$html = '
					<table class="basic_table">
						<tr>
							<th style="width:25%;">' . htmlspecialchars(ze\admin::phrase('Database field name')) . '</th>
							<th style="width:25%;">' . htmlspecialchars(ze\admin::phrase('Data type')) . '</th>
							<th style="width:25%;">' . htmlspecialchars(ze\admin::phrase('Security')) . '</th>
							<th style="width:25%;">' . htmlspecialchars(ze\admin::phrase('Comment')) . '</th>
						</tr>';
				while ($row = ze\sql::fetchAssoc($result)) {
					$table = $row['is_system_field'] ? $dataset['system_table'] : $dataset['table'];
					
					if ($row['db_column'] == 'password') {
						$security = 'Encrypted';
						$comment = '1-way encrypted';
					} elseif (ze::$dbL->columnIsEncrypted($table, $row['db_column'])) {
						$security = 'Encrypted';
						$comment = '';
					} elseif (in_array($row['db_column'], ['salutation', 'first_name', 'last_name', 'screen_name', 'identifier', 'email']) || (!$row['is_system_field'] && $row['type'] == 'text')) {
						$security = 'Not encrypted';
						$comment = 'Can encrypt';
					} else {
						$security = 'Not encrypted';
						$comment = '';
					}
					
					$html .= '
						<tr>
							<td>' . htmlspecialchars($row['db_column']) . '</td>
							<td>' . htmlspecialchars(ze\datasetAdm::getFieldTypeDescription($row['type'])) . '</td>
							<td>' . htmlspecialchars(ze\admin::phrase($security)) . '</td>
							<td>' . htmlspecialchars(ze\admin::phrase($comment)) . '</td>
						</tr>';
				}
				$html .= '
					<table>';
				$fields['data_encryption/dataset_fields']['snippet']['html'] = $html;
				
				//Show the number of records currently stored
				$count = ze\row::count('user_signin_log');
				$note = ze\admin::nPhrase('1 record currently stored.', '[[count]] records currently stored.', $count);
				
				if ($count) {
					$min = ze\row::min('user_signin_log', 'login_datetime');
					$note .= ' ' . ze\admin::phrase('Oldest record from [[date]].', ['date' => ze\admin::formatDateTime($min, '_MEDIUM')]);
				}
				
				$link = ze\link::absolute() . 'organizer.php#zenario__users/nav/sign_in_log/panel';
				$note .= ' ' . '<a target="_blank" href="' . $link . '">View</a>';
				$fields['data_protection/period_to_delete_sign_in_log']['note_below'] = $note;
				
				$count = ze\row::count('user_content_accesslog');
				$note = ze\admin::nPhrase('1 record currently stored.', '[[count]] records currently stored.', $count);
				
				if ($count) {
					$min = ze\row::min('user_content_accesslog', 'hit_datetime');
					$note .= ' ' . ze\admin::phrase('Oldest record from [[date]].', ['date' => ze\admin::formatDateTime($min, '_MEDIUM')]);
				}
				
				$link = ze\link::absolute() . 'organizer.php#zenario__users/panels/access_log';
				$note .= ' ' . '<a target="_blank" href="' . $link . '">View</a>';
				$fields['data_protection/period_to_delete_the_user_content_access_log']['note_below'] = $note;
				
				break;
		}
	}
	
	public function formatAdminBox($path, $settingGroup, &$box, &$fields, &$values, $changes) {
		switch ($settingGroup) {
			case 'users':
		
				$fields['unconfirmed_users/max_days_user_inactive']['hidden'] = !$values['unconfirmed_users/remove_inactive_users'];
		
				if ($values['inactive_user_email/time_user_inactive_1']){
					$fields['inactive_user_email/inactive_user_email_template_1']['hidden'] = false;
				} else{
					$fields['inactive_user_email/inactive_user_email_template_1']['hidden'] = true;
				}
	
				if ($values['inactive_user_email/time_user_inactive_2']){
					$fields['inactive_user_email/inactive_user_email_template_2']['hidden'] = false;
				} else{
					$fields['inactive_user_email/inactive_user_email_template_2']['hidden'] = true;
				}
				
				if ($values['passwords/min_extranet_user_password_score'] < 1) {
					$values['passwords/min_extranet_user_password_score'] = 1;
				} elseif ($values['passwords/min_extranet_user_password_score'] > 4) {
					$values['passwords/min_extranet_user_password_score'] = 4;
				}
				
				if (
					$values['passwords/min_extranet_user_password_score'] == 4
					&& (
						$values['passwords/min_extranet_user_password_score'] != $box['key']['current_password_min_score_value']
						|| ($values['passwords/min_extranet_user_password_length'] < 12 && empty($fields['passwords/min_extranet_user_password_length']['error']))
					)
				) {
					$values['passwords/min_extranet_user_password_length'] = 12;
				} elseif (
					$values['passwords/min_extranet_user_password_score'] == 3
					&& (
						$values['passwords/min_extranet_user_password_score'] != $box['key']['current_password_min_score_value']
						|| ($values['passwords/min_extranet_user_password_length'] < 10 && empty($fields['passwords/min_extranet_user_password_length']['error']))
					)
				) {
					$values['passwords/min_extranet_user_password_length'] = 10;
				} elseif (
					$values['passwords/min_extranet_user_password_score'] == 2
					&& (
						$values['passwords/min_extranet_user_password_score'] != $box['key']['current_password_min_score_value']
						|| ($values['passwords/min_extranet_user_password_length'] < 8 && empty($fields['passwords/min_extranet_user_password_length']['error']))
					)
				) {
					$values['passwords/min_extranet_user_password_length'] = 8;
				} elseif (
					$values['passwords/min_extranet_user_password_score'] == 1
					&& (
						$values['passwords/min_extranet_user_password_score'] != $box['key']['current_password_min_score_value']
						|| ($values['passwords/min_extranet_user_password_length'] < 6 && empty($fields['passwords/min_extranet_user_password_length']['error']))
					)
				) {
					$values['passwords/min_extranet_user_password_length'] = 6;
				}
				
				$box['key']['current_password_min_score_value'] = $values['passwords/min_extranet_user_password_score'];
				
				$cssClass = 'black';
				if ($values['passwords/min_extranet_user_password_score'] == 3 || $values['passwords/min_extranet_user_password_score'] == 4) {
					$cssClass = 'green';
				} elseif ($values['passwords/min_extranet_user_password_score'] == 2) {
					$cssClass = 'orange';
				} elseif ($values['passwords/min_extranet_user_password_score'] == 1) {
					$cssClass = 'red';
				}
				
				$fields['passwords/min_extranet_user_password_score']['slider']['class'] = $cssClass;
				
				break;
		}
	}
	
	
	public function validateAdminBox($path, $settingGroup, &$box, &$fields, &$values, $changes, $saving) {
		switch ($settingGroup) {
			case 'users':
				if ($values['inactive_user_email/time_user_inactive_1'] && !$values['inactive_user_email/inactive_user_email_template_1']) {
					$fields['inactive_user_email/inactive_user_email_template_1']['error'] = ze\admin::phrase('Please select an email template for the first period.');
				}
		
				if ($values['inactive_user_email/time_user_inactive_2'] && !$values['inactive_user_email/inactive_user_email_template_2']) {
					$fields['inactive_user_email/inactive_user_email_template_1']['error'] = ze\admin::phrase('Please select an email template for the second period.');
				}
				
				if ($values['passwords/min_extranet_user_password_length'] < 6 || $values['passwords/min_extranet_user_password_length'] > 32) {
					$fields['passwords/min_extranet_user_password_length']['error'] = ze\admin::phrase(
						'The minimum password length must be between [[min_password_length]] and [[max_password_length]].',
						['min_password_length' => 6, 'max_password_length' => 32]
					);
				} elseif (!ctype_digit($values['passwords/min_extranet_user_password_length'])) {
					$fields['passwords/min_extranet_user_password_length']['error'] = ze\admin::phrase('The value must be an integer.');
				}

				if ($values['passwords/min_extranet_user_password_score'] < 1 || $values['passwords/min_extranet_user_password_score'] > 4) {
					$fields['passwords/min_extranet_user_password_score']['error'] = ze\admin::phrase(
						'The minimum password score must be between [[min_password_score]] and [[max_password_score]].',
						['min_password_score' => 1, 'max_password_score' => 4]
					);
				} elseif (!ctype_digit($values['passwords/min_extranet_user_password_score'])) {
					$fields['passwords/min_extranet_user_password_score']['error'] = ze\admin::phrase('The value must be an integer.');
				}

				if (
					($values['passwords/min_extranet_user_password_score'] == 4 & $values['passwords/min_extranet_user_password_length'] < 12)
					|| ($values['passwords/min_extranet_user_password_score'] == 3 & $values['passwords/min_extranet_user_password_length'] < 10)
					|| ($values['passwords/min_extranet_user_password_score'] == 2 & $values['passwords/min_extranet_user_password_length'] < 8)
					|| ($values['passwords/min_extranet_user_password_score'] == 1 & $values['passwords/min_extranet_user_password_length'] < 6)
				) {
					if ($values['passwords/min_extranet_user_password_score'] == 4) {
						$requiredMinLength = 12;
					} elseif ($values['passwords/min_extranet_user_password_score'] == 3) {
						$requiredMinLength = 10;
					} elseif ($values['passwords/min_extranet_user_password_score'] == 2) {
						$requiredMinLength = 8;
					} elseif ($values['passwords/min_extranet_user_password_score'] == 1) {
						$requiredMinLength = 6;
					}
					
					$fields['passwords/min_extranet_user_password_length']['error'] = ze\admin::phrase(
						'To match score [[score]], the password length needs to be at least [[min_length]] characters.',
						['score' => $values['passwords/min_extranet_user_password_score'], 'min_length' => $requiredMinLength]
					);
				}
				
				//Max file upload size settings
				if ($values['groups/max_user_image_filesize_override']) {
					if ($values['groups/max_user_image_filesize'] && $values['groups/max_user_image_filesize_unit']) {
						$apacheMaxFilesize = ze\dbAdm::apacheMaxFilesize();
			
						$zenarioMaxFilesizeValue = ze::setting('content_max_filesize');
						$zenarioMaxFilesizeUnit = ze::setting('content_max_filesize_unit');
						$zenarioMaxFilesize = ze\file::fileSizeBasedOnUnit($zenarioMaxFilesizeValue, $zenarioMaxFilesizeUnit);
			
						$maxFileSize = min($apacheMaxFilesize, $zenarioMaxFilesize);
						$maxFileSizeFormatted = ze\file::fileSizeConvert($maxFileSize);
			
						$usersMaxFilesizeValue = $values['groups/max_user_image_filesize'];
						$usersMaxFilesizeUnit = $values['groups/max_user_image_filesize_unit'];
						$usersMaxFilesize = ze\file::fileSizeBasedOnUnit($usersMaxFilesizeValue, $usersMaxFilesizeUnit);
			
						if ($usersMaxFilesize > $maxFileSize) {
							$fields['groups/max_user_image_filesize']['error'] =
								ze\admin::phrase(
									'The maximum user image file size may not exceed [[global_max_attachment_file_size]].',
									['global_max_attachment_file_size' => $maxFileSizeFormatted]
								);
							$fields['groups/max_user_image_filesize_unit']['error'] = true;
						}
					}
				}
				
				if (isset($fields['activation_email_template/verification_email_expiry_period'])) {
					if ($values['activation_email_template/verification_email_expiry_period'] < 1 || $values['activation_email_template/verification_email_expiry_period'] > 48) {
						$fields['activation_email_template/verification_email_expiry_period']['error'] = ze\admin::phrase('Please enter a number between 1 and 48.');
					}
				}
				
				break;
		}
	}
	
	public function saveAdminBox($path, $settingGroup, &$box, &$fields, &$values, $changes) {
		switch ($settingGroup) {
			case 'users':
				if (ze\priv::check('_PRIV_EDIT_SITE_SETTING') && ze\ring::engToBoolean($box['tabs']['passwords']['edit_mode']['on'] ?? false)) {
					
				}
				
				break;
		}
	}
}