Compare commits
2 Commits
9270e457c6
...
c95bdf0ae5
Author | SHA1 | Date | |
---|---|---|---|
c95bdf0ae5 | |||
44a5d16921 |
@ -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>';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user