1
0
antisocial-safety/antisocial-safety.php

518 lines
16 KiB
PHP
Raw Permalink Normal View History

2024-10-10 19:10:14 +02:00
<?php
/**
* Plugin Name: antisocial-safety
* Description: Secures attachment uploads and comments with the OpenAI moderation endpoint
2024-10-11 17:45:18 +02:00
* Version: 1.7
2024-10-10 19:10:14 +02:00
*/
2024-10-11 17:45:18 +02:00
2024-10-10 19:10:14 +02:00
// Prevent direct access to the file
if (!defined('ABSPATH')) {
exit;
}
/**
* Global variable to store moderation results temporarily
*/
global $oai_moderation_results;
$oai_moderation_results = array();
/**
* Attachment Moderation
*/
// Hook into 'add_attachment' to moderate attachments upon upload
add_action('add_attachment', 'oai_moderate_attachment');
function oai_moderate_attachment($post_ID) {
$attachment = get_post($post_ID);
$mime_type = get_post_mime_type($post_ID);
// Only moderate images
if (strpos($mime_type, 'image/') !== false) {
// Get the API key
$api_key = get_option('oai_api_key');
if (!$api_key) {
// API key not set; skip moderation
return;
}
// Get the file URL
$file_url = wp_get_attachment_url($post_ID);
// Moderate the image
$result = oai_moderate_image($file_url);
if (isset($result['error'])) {
// Handle error (optional: log the error)
// For graceful failure, do not flag the attachment
return;
}
// Save the moderation result
update_post_meta($post_ID, '_oai_moderation_result', $result);
if ($result['flagged']) {
// Mark as flagged
update_post_meta($post_ID, '_oai_moderation_flagged', true);
2024-10-11 17:45:18 +02:00
// Delete the flagged attachment
wp_delete_attachment($post_ID, true);
// Set a transient to show an admin notice
set_transient('oai_flagged_upload_notice', __('An uploaded image was flagged by OpenAI Moderation and has been removed.', 'antisocial-safety'), 10);
// Optionally, you can log this event or take other actions
2024-10-10 19:10:14 +02:00
}
}
}
function oai_moderate_image($image_url) {
$api_key = get_option('oai_api_key');
if (!$api_key) {
// API key not set; skip moderation
return array();
}
$endpoint = 'https://api.openai.com/v1/moderations';
$data = array(
'model' => 'omni-moderation-latest',
'input' => array(
array(
'type' => 'image_url',
'image_url' => array('url' => $image_url),
),
),
);
$args = array(
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'body' => wp_json_encode($data),
'timeout' => 60,
);
$response = wp_remote_post($endpoint, $args);
if (is_wp_error($response)) {
// For graceful failure, return an empty array
2024-10-11 17:45:18 +02:00
return array('error' => $response->get_error_message());
2024-10-10 19:10:14 +02:00
}
$body = wp_remote_retrieve_body($response);
$result = json_decode($body, true);
if (isset($result['results'][0])) {
return $result['results'][0];
} else {
// Invalid response; return an empty array
return array();
}
}
2024-10-11 17:45:18 +02:00
// Display admin notice if an upload was flagged and removed
add_action('admin_notices', 'oai_flagged_upload_admin_notice');
function oai_flagged_upload_admin_notice() {
if ($message = get_transient('oai_flagged_upload_notice')) {
echo '<div class="notice notice-warning is-dismissible">
<p>' . esc_html($message) . '</p>
</div>';
2024-10-10 19:10:14 +02:00
}
}
/**
* Comments Moderation
*/
// Moderate comments before saving
add_filter('preprocess_comment', 'oai_moderate_comment');
function oai_moderate_comment($commentdata) {
$api_key = get_option('oai_api_key');
if (!$api_key) {
return $commentdata; // If API key not set, let the comment pass
}
$comment_content = $commentdata['comment_content'];
// Moderate the comment text
$result = oai_moderate_text($comment_content);
if (isset($result['error'])) {
// Handle error (optional: log the error)
// For graceful failure, let the comment pass
return $commentdata;
}
// Store the moderation result in a global variable indexed by comment content hash
$hash = md5($comment_content);
global $oai_moderation_results;
$oai_moderation_results[$hash] = $result;
return $commentdata;
}
// Set the comment approval status based on moderation result
add_filter('pre_comment_approved', 'oai_set_comment_approval_status', 10, 2);
function oai_set_comment_approval_status($approved, $commentdata) {
$api_key = get_option('oai_api_key');
if (!$api_key) {
return $approved; // Let the comment pass if API key not set
}
$comment_content = $commentdata['comment_content'];
$hash = md5($comment_content);
global $oai_moderation_results;
if (isset($oai_moderation_results[$hash])) {
$result = $oai_moderation_results[$hash];
if ($result['flagged']) {
// Set comment to unapproved
return '0'; // Unapproved
}
}
return $approved;
}
// Save the moderation result after the comment is inserted
add_action('comment_post', 'oai_save_comment_moderation_meta', 10, 2);
function oai_save_comment_moderation_meta($comment_ID, $comment_approved) {
$comment = get_comment($comment_ID);
$comment_content = $comment->comment_content;
$hash = md5($comment_content);
global $oai_moderation_results;
if (isset($oai_moderation_results[$hash])) {
$result = $oai_moderation_results[$hash];
update_comment_meta($comment_ID, '_oai_moderation_result', $result);
update_comment_meta($comment_ID, '_oai_moderation_flagged', $result['flagged'] ? 'pending' : 'ok');
// Remove from global variable
unset($oai_moderation_results[$hash]);
}
}
function oai_moderate_text($text) {
$api_key = get_option('oai_api_key');
if (!$api_key) {
// API key not set; skip moderation
return array(); // Return an empty array indicating no moderation
}
$endpoint = 'https://api.openai.com/v1/moderations';
$data = array(
'model' => 'omni-moderation-latest',
'input' => array(
array(
'type' => 'text',
'text' => $text,
),
),
);
$args = array(
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'body' => wp_json_encode($data),
'timeout' => 60,
);
$response = wp_remote_post($endpoint, $args);
if (is_wp_error($response)) {
// For graceful failure, return an empty array
2024-10-11 17:45:18 +02:00
return array('error' => $response->get_error_message());
2024-10-10 19:10:14 +02:00
}
$body = wp_remote_retrieve_body($response);
$result = json_decode($body, true);
if (isset($result['results'][0])) {
return $result['results'][0];
} else {
// Invalid response; return an empty array
return array();
}
}
2024-10-11 17:45:18 +02:00
/**
* Anonymizer Functionality
*/
// Hook into 'wp_handle_upload_prefilter' to anonymize filenames if enabled
add_filter('wp_handle_upload_prefilter', 'oai_anonymize_filename');
function oai_anonymize_filename($file) {
// Check if anonymizer is enabled
$anonymizer_enabled = get_option('oai_enable_anonymizer', false);
if (!$anonymizer_enabled) {
return $file;
}
// Get the file extension
$file_info = pathinfo($file['name']);
$extension = isset($file_info['extension']) ? '.' . strtolower($file_info['extension']) : '';
// Generate a unique 10-digit filename
$new_filename = oai_generate_unique_filename($extension);
// Replace the original filename with the new anonymized filename
$file['name'] = $new_filename;
return $file;
}
function oai_generate_unique_filename($extension) {
$upload_dir = wp_upload_dir();
$base_dir = trailingslashit($upload_dir['basedir']);
do {
// Generate a random number between 0000000000 and 9999999999
$random_number = str_pad(mt_rand(0, 9999999999), 10, '0', STR_PAD_LEFT);
$new_filename = $random_number . $extension;
$file_path = $base_dir . $new_filename;
} while (file_exists($file_path));
return $new_filename;
}
2024-10-10 19:10:14 +02:00
/**
* Admin Columns for Media and Comments
*/
// Add a custom column to the media library
add_filter('manage_media_columns', 'oai_add_media_column');
function oai_add_media_column($columns) {
2024-10-11 17:45:18 +02:00
$columns['oai_moderation'] = __('Moderation', 'antisocial-safety');
2024-10-10 19:10:14 +02:00
return $columns;
}
add_action('manage_media_custom_column', 'oai_media_column_content', 10, 2);
function oai_media_column_content($column_name, $post_ID) {
if ($column_name == 'oai_moderation') {
2024-10-11 17:45:18 +02:00
$moderation_result = get_post_meta($post_ID, '_oai_moderation_result', true);
2024-10-10 19:10:14 +02:00
$flagged = get_post_meta($post_ID, '_oai_moderation_flagged', true);
if ($flagged) {
2024-10-11 17:45:18 +02:00
echo '<span style="color:red;">' . __('Flagged', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
} else {
2024-10-11 17:45:18 +02:00
echo '<span style="color:green;">' . __('OK', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
}
}
}
// Add a custom column to the comments list
add_filter('manage_edit-comments_columns', 'oai_add_comments_column');
function oai_add_comments_column($columns) {
2024-10-11 17:45:18 +02:00
$columns['oai_moderation'] = __('Moderation', 'antisocial-safety');
2024-10-10 19:10:14 +02:00
return $columns;
}
add_action('manage_comments_custom_column', 'oai_comments_column_content', 10, 2);
function oai_comments_column_content($column_name, $comment_ID) {
if ($column_name == 'oai_moderation') {
$flagged = get_comment_meta($comment_ID, '_oai_moderation_flagged', true);
if ($flagged === 'pending') {
2024-10-11 17:45:18 +02:00
echo '<span style="color:orange;">' . __('Pending Approval', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
} elseif ($flagged === 'flagged') {
2024-10-11 17:45:18 +02:00
echo '<span style="color:red;">' . __('Flagged', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
} else {
2024-10-11 17:45:18 +02:00
echo '<span style="color:green;">' . __('OK', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
}
}
}
/**
* Display Moderation Details in Admin Screens
*/
// Add moderation info to the attachment edit screen
add_filter('attachment_fields_to_edit', 'oai_attachment_fields', 10, 2);
function oai_attachment_fields($form_fields, $post) {
$api_key = get_option('oai_api_key');
if (!$api_key) {
// API key not set; do not display moderation fields
return $form_fields;
}
$flagged = get_post_meta($post->ID, '_oai_moderation_flagged', true);
$moderation_result = get_post_meta($post->ID, '_oai_moderation_result', true);
if ($flagged) {
$form_fields['oai_moderation'] = array(
2024-10-11 17:45:18 +02:00
'label' => __('OpenAI Moderation', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'input' => 'html',
2024-10-11 17:45:18 +02:00
'html' => '<span style="color:red;">' . __('This attachment was flagged by OpenAI Moderation and has been removed.', 'antisocial-safety') . '</span>',
'helps' => __('Flagged content is blocked from being served to users.', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
);
} else {
$form_fields['oai_moderation'] = array(
2024-10-11 17:45:18 +02:00
'label' => __('OpenAI Moderation', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'input' => 'html',
2024-10-11 17:45:18 +02:00
'html' => '<span style="color:green;">' . __('This attachment passed OpenAI Moderation.', 'antisocial-safety') . '</span>',
2024-10-10 19:10:14 +02:00
);
}
if ($moderation_result) {
$form_fields['oai_moderation_result'] = array(
2024-10-11 17:45:18 +02:00
'label' => __('Moderation Details', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'input' => 'html',
'html' => '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>',
);
}
return $form_fields;
}
// Add a meta box to the comment edit screen for moderation details
add_action('add_meta_boxes_comment', 'oai_add_comment_meta_box');
function oai_add_comment_meta_box() {
add_meta_box(
'oai_comment_moderation',
2024-10-11 17:45:18 +02:00
__('OpenAI Moderation Details', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'oai_comment_moderation_meta_box',
'comment',
'normal',
'high'
);
}
function oai_comment_moderation_meta_box($comment) {
$api_key = get_option('oai_api_key');
if (!$api_key) {
// API key not set; do not display moderation details
return;
}
$moderation_result = get_comment_meta($comment->comment_ID, '_oai_moderation_result', true);
$flagged = get_comment_meta($comment->comment_ID, '_oai_moderation_flagged', true);
if ($flagged) {
2024-10-11 17:45:18 +02:00
echo '<p><strong>' . __('Status:', 'antisocial-safety') . '</strong> ';
2024-10-10 19:10:14 +02:00
if ($flagged === 'pending') {
2024-10-11 17:45:18 +02:00
echo '<span style="color:orange;">' . __('Pending Approval', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
} elseif ($flagged === 'flagged') {
2024-10-11 17:45:18 +02:00
echo '<span style="color:red;">' . __('Flagged', 'antisocial-safety') . '</span>';
2024-10-10 19:10:14 +02:00
}
echo '</p>';
if ($moderation_result) {
2024-10-11 17:45:18 +02:00
echo '<h4>' . __('Moderation Details', 'antisocial-safety') . '</h4>';
2024-10-10 19:10:14 +02:00
echo '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>';
}
} else {
2024-10-11 17:45:18 +02:00
echo '<p><span style="color:green;">' . __('This comment passed OpenAI Moderation.', 'antisocial-safety') . '</span></p>';
2024-10-10 19:10:14 +02:00
}
}
/**
* Settings Page
*/
// Add settings page under Settings menu
add_action('admin_menu', 'oai_add_admin_menu');
function oai_add_admin_menu() {
add_options_page(
2024-10-11 17:45:18 +02:00
__('OpenAI Moderation', 'antisocial-safety'),
__('OpenAI Moderation', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'manage_options',
'openai-moderation',
'oai_options_page'
);
}
function oai_options_page() {
?>
<div class="wrap">
2024-10-11 17:45:18 +02:00
<h1><?php esc_html_e('OpenAI Moderation Settings', 'antisocial-safety'); ?></h1>
2024-10-10 19:10:14 +02:00
<form method="post" action="options.php">
<?php
settings_fields('oai_options_group');
do_settings_sections('openai-moderation');
submit_button();
?>
</form>
</div>
<?php
}
// Initialize settings
add_action('admin_init', 'oai_settings_init');
function oai_settings_init() {
register_setting('oai_options_group', 'oai_api_key');
2024-10-11 17:45:18 +02:00
register_setting('oai_options_group', 'oai_enable_anonymizer');
2024-10-10 19:10:14 +02:00
add_settings_section(
'oai_settings_section',
2024-10-11 17:45:18 +02:00
__('OpenAI API Settings', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'oai_settings_section_callback',
'openai-moderation'
);
add_settings_field(
'oai_api_key_field',
2024-10-11 17:45:18 +02:00
__('OpenAI API Key', 'antisocial-safety'),
2024-10-10 19:10:14 +02:00
'oai_api_key_render',
'openai-moderation',
'oai_settings_section'
);
2024-10-11 17:45:18 +02:00
add_settings_field(
'oai_enable_anonymizer_field',
__('Enable Filename Anonymizer', 'antisocial-safety'),
'oai_enable_anonymizer_render',
'openai-moderation',
'oai_settings_section'
);
2024-10-10 19:10:14 +02:00
}
function oai_settings_section_callback() {
2024-10-11 17:45:18 +02:00
echo '<p>' . esc_html__('Enter your OpenAI API key to enable moderation. You can also toggle the filename anonymizer below.', 'antisocial-safety') . '</p>';
2024-10-10 19:10:14 +02:00
}
function oai_api_key_render() {
$api_key = get_option('oai_api_key');
?>
<input type="text" name="oai_api_key" value="<?php echo esc_attr($api_key); ?>" size="50">
<?php
}
2024-10-11 17:45:18 +02:00
function oai_enable_anonymizer_render() {
$enabled = get_option('oai_enable_anonymizer', false);
?>
<input type="checkbox" name="oai_enable_anonymizer" value="1" <?php checked(1, $enabled, true); ?> />
<label for="oai_enable_anonymizer"><?php esc_html_e('Rename uploaded files to unique 10-digit random numbers.', 'antisocial-safety'); ?></label>
<?php
}
2024-10-10 19:10:14 +02:00
// Display admin notice if API key is not set
add_action('admin_notices', 'oai_admin_notices');
function oai_admin_notices() {
if (!current_user_can('manage_options')) {
return;
}
// Check if we're on the OpenAI Moderation settings page
$screen = get_current_screen();
if ($screen->id !== 'settings_page_openai-moderation') {
return;
}
$api_key = get_option('oai_api_key');
if (!$api_key) {
echo '<div class="notice notice-warning is-dismissible">
2024-10-11 17:45:18 +02:00
<p>' . __('OpenAI API key is not set. Moderation features are disabled until you enter a valid API key.', 'antisocial-safety') . '</p>
2024-10-10 19:10:14 +02:00
</div>';
}
2024-10-11 17:45:18 +02:00
}