Creating Emails with AI
Tutorial: Creating Emails with AI
This guide explains the functionality and code of the action_create_mail_ai.php script. This script allows users to generate a complete email draft using an AI model, simply by providing a topic or subject line.
How It Works: The User's Journey
The process is designed to be simple and intuitive for the end-user:
- Step 1: Provide a Topic
The user clicks the custom button, and a dialog box appears asking for the email's topic (e.g., "Invitation to the team meeting on Friday"). - Step 2: AI Generation
After confirming, a loading spinner is shown while the topic is sent to the backend. The PHP script then prompts the OpenAI API to write an email based on this topic. - Step 3: Save to Database
The AI's HTML-formatted response is received by the backend. The script then creates a new record in the current database table, populating fields likebetreff(subject),datum(date), andtext(the AI-generated HTML). - Step 4: Edit and Finalize
The user is automatically redirected to the edit screen for the newly created email record, where they can review, modify, and finalize the draft.
Code Deep Dive
The script is divided into two main parts: the PHP backend for processing and the JavaScript frontend for user interaction.
PHP Backend Logic
The PHP code handles security, communicates with the AI, and interacts with the database.
1. Initialization and Security
The script starts by including necessary files, checking for an active user session, and verifying that the user has permission to add records to the specified table.
require_once '../init.php';
require_once 'ai_helpers.php';
// --- Access Control & Initialization ---
if (!isset($_SESSION['user'])) {
api_error_response(403, 'AUTH_REQUIRED', 'Not authenticated.');
}
$table_id = (int)($_GET['table_id'] ?? 0);
// ... permission checks ...
$table_definition = DBGet('custom_tables', $table_id);
if (!$table_definition || !check_user_permission($table_definition['table_name'], 'can_add')) {
api_error_response(403, 'PERMISSION_DENIED', 'No permission to add records.');
}
2. The AI API Call: call_openai_api_for_text()
This is a specialized function to communicate with the OpenAI API. Unlike other helpers that might expect a JSON response, this one is designed to retrieve raw text or HTML content.
function call_openai_api_for_text(string $system_prompt, string $user_prompt, string $api_key): string {
// ... cURL setup ...
$data = [
'model' => 'gpt-4o',
'messages' => [
['role' => 'system', 'content' => $system_prompt],
['role' => 'user', 'content' => $user_prompt],
],
'temperature' => 0.7,
];
// ... cURL execution and error handling ...
$result = json_decode($response, true);
// ... Error checking for HTTP status and API errors ...
return $result['choices'][0]['message']['content'] ?? '';
}
3. Handling the POST Request
This is the core of the backend logic, executed when the frontend sends the user's topic.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
check_csrf_token();
$mail_topic = trim($_POST['mail_topic'] ?? '');
if (empty($mail_topic)) {
throw new Exception('The email topic cannot be empty.');
}
// 1. Prepare the AI prompts
$system_prompt = "You are a helpful assistant who writes professional emails. The user provides a topic. Write a suitable email in HTML format. Return only the HTML body of the email, without surrounding text, comments, or explanations.";
$user_prompt = "Write a polite and professional email on the following topic: '{$mail_topic}'. Return the response as a complete HTML body.";
// 2. Call the AI
$ai_response_html = call_openai_api_for_text($system_prompt, $user_prompt, $apiKey);
// 3. Clean the AI response
if (preg_match('/```html\s*(.*?)\s*```/s', $ai_response_html, $matches)) {
$ai_response_html = $matches[1];
}
// 4. Prepare and insert the new record
$record_data = [
'betreff' => $mail_topic,
'datum' => date('Y-m-d H:i:s'),
'team' => $_SESSION['user']['team'],
'text' => $ai_response_html,
'created_by_user_id' => $_SESSION['user']['id']
];
$new_record_id = DBInsert($physical_table_name, array_keys($record_data), array_values($record_data));
// ... error handling for DBInsert ...
// 5. Send success response with redirect URL
header('Content-Type: application/json');
echo json_encode([
'success' => true,
'data' => [
'redirect' => "custom_table_record_edit.php?table_id={$table_id}&id={$new_record_id}"
]
]);
exit();
} catch (Exception $e) {
api_error_response(500, 'AI_PROCESSING_ERROR', 'Error during AI processing: ' . $e->getMessage());
}
}
preg_match function is crucial. AI models often wrap code snippets in Markdown blocks (e.g., ```html...```). This regular expression extracts only the clean HTML content, ensuring no extra characters are saved to the database.
JavaScript Frontend Logic
The inline <script> tag at the end of the file handles all user interactions within the browser.
(async () => {
// Step 1: Show a dialog to get the topic from the user.
const confirmed = await showDialog({
title: '<i class="fa-solid fa-robot me-2"></i> Create New Email via AI',
text: `
<p>Enter the topic or subject for the new email here.</p>
<input type="text" id="mailTopicInput" class="form-control" placeholder="e.g., Invitation to team meeting on Friday">
`,
confirmButtonText: 'Create Email »',
cancelButtonText: 'Cancel',
width: '600px',
});
if (!confirmed) return; // User cancelled
const mailTopic = document.getElementById('mailTopicInput').value.trim();
if (!mailTopic) {
// Show an error if the input is empty
setTimeout(() => showDialog({ title: 'Error', text: 'Please provide a topic for the email!', okText: 'OK' }), 200);
return;
}
// Step 2: Show a loading spinner.
showDialog({ title: 'Generating Email...', text: '<div class="text-center p-4"><div class="spinner-border text-primary"></div></div>', okText: '' });
try {
// Step 3: Send the data to the PHP backend.
const postUrl = '<?= $_SERVER['PHP_SELF'] ?>?table_id=<?= $table_id ?>';
const formData = new FormData();
formData.append('mail_topic', mailTopic);
formData.append('csrf_token', '<?= generate_csrf_token() ?>');
const response = await fetch(postUrl, {
method: 'POST',
body: formData
});
const result = await response.json();
// Close the loading dialog
const loadingModalElement = document.getElementById('dynamicModal');
if (loadingModalElement) {
bootstrap.Modal.getInstance(loadingModalElement)?.hide();
}
if (!response.ok || !result.success) {
throw new Error(result.message || 'An unknown error occurred.');
}
// Step 4: Redirect on success.
if (result.data && result.data.redirect) {
window.location.href = result.data.redirect;
}
} catch (error) {
// ... error handling to close spinner and show an error dialog ...
}
})();
The frontend script uses the existing showDialog() helper function for all user interactions. It makes an asynchronous fetch call to its own PHP file, handles the JSON response, and performs the final redirection, providing a seamless experience.