1
0

Compare commits

...

2 Commits

Author SHA1 Message Date
c95bdf0ae5 updated readme 2024-10-11 15:47:21 +00:00
44a5d16921 added attachment anonymizing feature 2024-10-11 15:45:18 +00:00
2 changed files with 107 additions and 51 deletions

View File

@ -2,9 +2,10 @@
/** /**
* Plugin Name: antisocial-safety * Plugin Name: antisocial-safety
* Description: Secures attachment uploads and comments with the OpenAI moderation endpoint * Description: Secures attachment uploads and comments with the OpenAI moderation endpoint
* Version: 1.6 * Version: 1.7
*/ */
// Prevent direct access to the file // Prevent direct access to the file
if (!defined('ABSPATH')) { if (!defined('ABSPATH')) {
exit; exit;
@ -54,9 +55,14 @@ function oai_moderate_attachment($post_ID) {
if ($result['flagged']) { if ($result['flagged']) {
// Mark as flagged // Mark as flagged
update_post_meta($post_ID, '_oai_moderation_flagged', true); update_post_meta($post_ID, '_oai_moderation_flagged', true);
} else {
// Remove any previous flag // Delete the flagged attachment
delete_post_meta($post_ID, '_oai_moderation_flagged'); 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
} }
} }
} }
@ -93,7 +99,7 @@ function oai_moderate_image($image_url) {
if (is_wp_error($response)) { if (is_wp_error($response)) {
// For graceful failure, return an empty array // For graceful failure, return an empty array
return array(); return array('error' => $response->get_error_message());
} }
$body = wp_remote_retrieve_body($response); $body = wp_remote_retrieve_body($response);
@ -107,22 +113,14 @@ function oai_moderate_image($image_url) {
} }
} }
// Hook into 'template_redirect' to block access to flagged attachments // Display admin notice if an upload was flagged and removed
add_action('template_redirect', 'oai_block_flagged_attachments'); add_action('admin_notices', 'oai_flagged_upload_admin_notice');
function oai_block_flagged_attachments() { function oai_flagged_upload_admin_notice() {
if (is_attachment()) { if ($message = get_transient('oai_flagged_upload_notice')) {
global $post; echo '<div class="notice notice-warning is-dismissible">
if ($post) { <p>' . esc_html($message) . '</p>
$flagged = get_post_meta($post->ID, '_oai_moderation_flagged', true); </div>';
if ($flagged) {
// Send 403 Forbidden header and exit
status_header(403);
nocache_headers();
// Optionally, display a message
wp_die(__('You are not allowed to access this content.', 'oai'), __('Forbidden', 'oai'), array('response' => 403));
}
}
} }
} }
@ -231,7 +229,7 @@ function oai_moderate_text($text) {
if (is_wp_error($response)) { if (is_wp_error($response)) {
// For graceful failure, return an empty array // For graceful failure, return an empty array
return array(); return array('error' => $response->get_error_message());
} }
$body = wp_remote_retrieve_body($response); $body = wp_remote_retrieve_body($response);
@ -245,6 +243,47 @@ function oai_moderate_text($text) {
} }
} }
/**
* 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;
}
/** /**
* Admin Columns for Media and Comments * Admin Columns for Media and Comments
*/ */
@ -252,18 +291,19 @@ function oai_moderate_text($text) {
// Add a custom column to the media library // Add a custom column to the media library
add_filter('manage_media_columns', 'oai_add_media_column'); add_filter('manage_media_columns', 'oai_add_media_column');
function oai_add_media_column($columns) { function oai_add_media_column($columns) {
$columns['oai_moderation'] = __('Moderation', 'oai'); $columns['oai_moderation'] = __('Moderation', 'antisocial-safety');
return $columns; return $columns;
} }
add_action('manage_media_custom_column', 'oai_media_column_content', 10, 2); add_action('manage_media_custom_column', 'oai_media_column_content', 10, 2);
function oai_media_column_content($column_name, $post_ID) { function oai_media_column_content($column_name, $post_ID) {
if ($column_name == 'oai_moderation') { if ($column_name == 'oai_moderation') {
$moderation_result = get_post_meta($post_ID, '_oai_moderation_result', true);
$flagged = get_post_meta($post_ID, '_oai_moderation_flagged', true); $flagged = get_post_meta($post_ID, '_oai_moderation_flagged', true);
if ($flagged) { if ($flagged) {
echo '<span style="color:red;">' . __('Flagged', 'oai') . '</span>'; echo '<span style="color:red;">' . __('Flagged', 'antisocial-safety') . '</span>';
} else { } else {
echo '<span style="color:green;">' . __('OK', 'oai') . '</span>'; echo '<span style="color:green;">' . __('OK', 'antisocial-safety') . '</span>';
} }
} }
} }
@ -271,7 +311,7 @@ function oai_media_column_content($column_name, $post_ID) {
// Add a custom column to the comments list // Add a custom column to the comments list
add_filter('manage_edit-comments_columns', 'oai_add_comments_column'); add_filter('manage_edit-comments_columns', 'oai_add_comments_column');
function oai_add_comments_column($columns) { function oai_add_comments_column($columns) {
$columns['oai_moderation'] = __('Moderation', 'oai'); $columns['oai_moderation'] = __('Moderation', 'antisocial-safety');
return $columns; return $columns;
} }
@ -280,11 +320,11 @@ function oai_comments_column_content($column_name, $comment_ID) {
if ($column_name == 'oai_moderation') { if ($column_name == 'oai_moderation') {
$flagged = get_comment_meta($comment_ID, '_oai_moderation_flagged', true); $flagged = get_comment_meta($comment_ID, '_oai_moderation_flagged', true);
if ($flagged === 'pending') { if ($flagged === 'pending') {
echo '<span style="color:orange;">' . __('Pending Approval', 'oai') . '</span>'; echo '<span style="color:orange;">' . __('Pending Approval', 'antisocial-safety') . '</span>';
} elseif ($flagged === 'flagged') { } elseif ($flagged === 'flagged') {
echo '<span style="color:red;">' . __('Flagged', 'oai') . '</span>'; echo '<span style="color:red;">' . __('Flagged', 'antisocial-safety') . '</span>';
} else { } else {
echo '<span style="color:green;">' . __('OK', 'oai') . '</span>'; echo '<span style="color:green;">' . __('OK', 'antisocial-safety') . '</span>';
} }
} }
} }
@ -307,22 +347,22 @@ function oai_attachment_fields($form_fields, $post) {
if ($flagged) { if ($flagged) {
$form_fields['oai_moderation'] = array( $form_fields['oai_moderation'] = array(
'label' => __('OpenAI Moderation', 'oai'), 'label' => __('OpenAI Moderation', 'antisocial-safety'),
'input' => 'html', 'input' => 'html',
'html' => '<span style="color:red;">' . __('This attachment has been flagged by OpenAI Moderation.', 'oai') . '</span>', '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.', 'oai'), 'helps' => __('Flagged content is blocked from being served to users.', 'antisocial-safety'),
); );
} else { } else {
$form_fields['oai_moderation'] = array( $form_fields['oai_moderation'] = array(
'label' => __('OpenAI Moderation', 'oai'), 'label' => __('OpenAI Moderation', 'antisocial-safety'),
'input' => 'html', 'input' => 'html',
'html' => '<span style="color:green;">' . __('This attachment passed OpenAI Moderation.', 'oai') . '</span>', 'html' => '<span style="color:green;">' . __('This attachment passed OpenAI Moderation.', 'antisocial-safety') . '</span>',
); );
} }
if ($moderation_result) { if ($moderation_result) {
$form_fields['oai_moderation_result'] = array( $form_fields['oai_moderation_result'] = array(
'label' => __('Moderation Details', 'oai'), 'label' => __('Moderation Details', 'antisocial-safety'),
'input' => 'html', 'input' => 'html',
'html' => '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>', 'html' => '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>',
); );
@ -336,7 +376,7 @@ add_action('add_meta_boxes_comment', 'oai_add_comment_meta_box');
function oai_add_comment_meta_box() { function oai_add_comment_meta_box() {
add_meta_box( add_meta_box(
'oai_comment_moderation', 'oai_comment_moderation',
__('OpenAI Moderation Details', 'oai'), __('OpenAI Moderation Details', 'antisocial-safety'),
'oai_comment_moderation_meta_box', 'oai_comment_moderation_meta_box',
'comment', 'comment',
'normal', 'normal',
@ -355,20 +395,20 @@ function oai_comment_moderation_meta_box($comment) {
$flagged = get_comment_meta($comment->comment_ID, '_oai_moderation_flagged', true); $flagged = get_comment_meta($comment->comment_ID, '_oai_moderation_flagged', true);
if ($flagged) { if ($flagged) {
echo '<p><strong>' . __('Status:', 'oai') . '</strong> '; echo '<p><strong>' . __('Status:', 'antisocial-safety') . '</strong> ';
if ($flagged === 'pending') { if ($flagged === 'pending') {
echo '<span style="color:orange;">' . __('Pending Approval', 'oai') . '</span>'; echo '<span style="color:orange;">' . __('Pending Approval', 'antisocial-safety') . '</span>';
} elseif ($flagged === 'flagged') { } elseif ($flagged === 'flagged') {
echo '<span style="color:red;">' . __('Flagged', 'oai') . '</span>'; echo '<span style="color:red;">' . __('Flagged', 'antisocial-safety') . '</span>';
} }
echo '</p>'; echo '</p>';
if ($moderation_result) { if ($moderation_result) {
echo '<h4>' . __('Moderation Details', 'oai') . '</h4>'; echo '<h4>' . __('Moderation Details', 'antisocial-safety') . '</h4>';
echo '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>'; echo '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>';
} }
} else { } else {
echo '<p><span style="color:green;">' . __('This comment passed OpenAI Moderation.', 'oai') . '</span></p>'; echo '<p><span style="color:green;">' . __('This comment passed OpenAI Moderation.', 'antisocial-safety') . '</span></p>';
} }
} }
@ -381,8 +421,8 @@ add_action('admin_menu', 'oai_add_admin_menu');
function oai_add_admin_menu() { function oai_add_admin_menu() {
add_options_page( add_options_page(
__('OpenAI Moderation', 'oai'), __('OpenAI Moderation', 'antisocial-safety'),
__('OpenAI Moderation', 'oai'), __('OpenAI Moderation', 'antisocial-safety'),
'manage_options', 'manage_options',
'openai-moderation', 'openai-moderation',
'oai_options_page' 'oai_options_page'
@ -392,7 +432,7 @@ function oai_add_admin_menu() {
function oai_options_page() { function oai_options_page() {
?> ?>
<div class="wrap"> <div class="wrap">
<h1><?php esc_html_e('OpenAI Moderation Settings', 'oai'); ?></h1> <h1><?php esc_html_e('OpenAI Moderation Settings', 'antisocial-safety'); ?></h1>
<form method="post" action="options.php"> <form method="post" action="options.php">
<?php <?php
settings_fields('oai_options_group'); settings_fields('oai_options_group');
@ -409,25 +449,34 @@ add_action('admin_init', 'oai_settings_init');
function oai_settings_init() { function oai_settings_init() {
register_setting('oai_options_group', 'oai_api_key'); register_setting('oai_options_group', 'oai_api_key');
register_setting('oai_options_group', 'oai_enable_anonymizer');
add_settings_section( add_settings_section(
'oai_settings_section', 'oai_settings_section',
__('OpenAI API Settings', 'oai'), __('OpenAI API Settings', 'antisocial-safety'),
'oai_settings_section_callback', 'oai_settings_section_callback',
'openai-moderation' 'openai-moderation'
); );
add_settings_field( add_settings_field(
'oai_api_key_field', 'oai_api_key_field',
__('OpenAI API Key', 'oai'), __('OpenAI API Key', 'antisocial-safety'),
'oai_api_key_render', 'oai_api_key_render',
'openai-moderation', 'openai-moderation',
'oai_settings_section' 'oai_settings_section'
); );
add_settings_field(
'oai_enable_anonymizer_field',
__('Enable Filename Anonymizer', 'antisocial-safety'),
'oai_enable_anonymizer_render',
'openai-moderation',
'oai_settings_section'
);
} }
function oai_settings_section_callback() { function oai_settings_section_callback() {
echo '<p>' . esc_html__('Enter your OpenAI API key to enable moderation.', 'oai') . '</p>'; echo '<p>' . esc_html__('Enter your OpenAI API key to enable moderation. You can also toggle the filename anonymizer below.', 'antisocial-safety') . '</p>';
} }
function oai_api_key_render() { function oai_api_key_render() {
@ -437,6 +486,14 @@ function oai_api_key_render() {
<?php <?php
} }
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
}
// Display admin notice if API key is not set // Display admin notice if API key is not set
add_action('admin_notices', 'oai_admin_notices'); add_action('admin_notices', 'oai_admin_notices');
@ -454,7 +511,7 @@ function oai_admin_notices() {
$api_key = get_option('oai_api_key'); $api_key = get_option('oai_api_key');
if (!$api_key) { if (!$api_key) {
echo '<div class="notice notice-warning is-dismissible"> echo '<div class="notice notice-warning is-dismissible">
<p>' . __('OpenAI API key is not set. Moderation features are disabled until you enter a valid API key.', 'oai') . '</p> <p>' . __('OpenAI API key is not set. Moderation features are disabled until you enter a valid API key.', 'antisocial-safety') . '</p>
</div>'; </div>';
} }
} }

View File

@ -8,7 +8,7 @@ it keeps your site clean by blocking offensive or illegal images and comments be
## features ## features
- **attachment moderation**: images are checked during upload. flagged images are blocked from being accessed. - **attachment moderation**: images are checked during upload. flagged images are blocked. optionally, anonymize file names.
- **comment moderation**: comments are reviewed before they're saved. flagged comments are set to unapproved. - **comment moderation**: comments are reviewed before they're saved. flagged comments are set to unapproved.
- **admin integration**: see moderation status directly in your media library and comments list. detailed info available in edit screens. - **admin integration**: see moderation status directly in your media library and comments list. detailed info available in edit screens.
- **easy setup**: just add your openai api key in the settings. - **easy setup**: just add your openai api key in the settings.
@ -24,7 +24,7 @@ it keeps your site clean by blocking offensive or illegal images and comments be
### attachment moderation ### attachment moderation
- when an image is uploaded, the plugin sends it to openai's moderation api using the `omni-moderation-latest` model. - when an image is uploaded, the plugin sends it to openai's moderation api using the `omni-moderation-latest` model.
- if the image is flagged, the plugin blocks access to it by sending a 403 forbidden response when someone tries to view it. - if the image is flagged in any of the abuse categories, the attachment does not get created.
### comment moderation ### comment moderation
@ -34,7 +34,6 @@ it keeps your site clean by blocking offensive or illegal images and comments be
### backend functionality ### backend functionality
- hooks into `add_attachment` to moderate images upon upload. - hooks into `add_attachment` to moderate images upon upload.
- uses `template_redirect` to block access to flagged attachments.
- hooks into `preprocess_comment` and `pre_comment_approved` to moderate comments before saving. - hooks into `preprocess_comment` and `pre_comment_approved` to moderate comments before saving.
- stores moderation results in post and comment meta. - stores moderation results in post and comment meta.
- adds custom columns in admin screens to display moderation status. - adds custom columns in admin screens to display moderation status.