Changeset 3326772
- Timestamp:
- 07/12/2025 01:33:21 PM (7 months ago)
- Location:
- moderation-api-automated-content-moderation
- Files:
-
- 27 added
- 3 deleted
- 25 edited
- 2 copied
-
tags/1.0.3 (copied) (copied from moderation-api-automated-content-moderation/trunk)
-
tags/1.0.3/CLAUDE.md (added)
-
tags/1.0.3/README.txt (added)
-
tags/1.0.3/admin/class-moderation-api-admin.php (modified) (22 diffs)
-
tags/1.0.3/admin/class-moderation-api-network-admin.php (added)
-
tags/1.0.3/admin/css/moderation-api-admin.css (modified) (6 diffs)
-
tags/1.0.3/admin/views/config.php (modified) (6 diffs)
-
tags/1.0.3/admin/views/network (added)
-
tags/1.0.3/admin/views/network/activate.php (added)
-
tags/1.0.3/admin/views/network/config.php (added)
-
tags/1.0.3/admin/views/network/get.php (added)
-
tags/1.0.3/admin/views/network/how-it-works.php (added)
-
tags/1.0.3/admin/views/network/start.php (added)
-
tags/1.0.3/development.md (modified) (1 diff)
-
tags/1.0.3/includes/class-moderation-api-activator.php (modified) (1 diff)
-
tags/1.0.3/includes/class-moderation-api.php (modified) (17 diffs)
-
tags/1.0.3/moderation-api.php (modified) (3 diffs)
-
tags/1.0.3/public/class-moderation-api-public.php (modified) (12 diffs)
-
tags/1.0.3/readme.txt (deleted)
-
tags/1.0.3/trunk (copied) (copied from moderation-api-automated-content-moderation/trunk)
-
tags/1.0.3/trunk/CLAUDE.md (added)
-
tags/1.0.3/trunk/README.txt (added)
-
tags/1.0.3/trunk/admin/class-moderation-api-admin.php (modified) (22 diffs)
-
tags/1.0.3/trunk/admin/class-moderation-api-network-admin.php (added)
-
tags/1.0.3/trunk/admin/css/moderation-api-admin.css (modified) (6 diffs)
-
tags/1.0.3/trunk/admin/views/config.php (modified) (6 diffs)
-
tags/1.0.3/trunk/admin/views/network (added)
-
tags/1.0.3/trunk/admin/views/network/activate.php (added)
-
tags/1.0.3/trunk/admin/views/network/config.php (added)
-
tags/1.0.3/trunk/admin/views/network/get.php (added)
-
tags/1.0.3/trunk/admin/views/network/how-it-works.php (added)
-
tags/1.0.3/trunk/admin/views/network/start.php (added)
-
tags/1.0.3/trunk/development.md (modified) (1 diff)
-
tags/1.0.3/trunk/includes/class-moderation-api-activator.php (modified) (1 diff)
-
tags/1.0.3/trunk/includes/class-moderation-api.php (modified) (17 diffs)
-
tags/1.0.3/trunk/moderation-api.php (modified) (3 diffs)
-
tags/1.0.3/trunk/public/class-moderation-api-public.php (modified) (12 diffs)
-
tags/1.0.3/trunk/readme.txt (deleted)
-
trunk/CLAUDE.md (added)
-
trunk/README.txt (added)
-
trunk/admin/class-moderation-api-admin.php (modified) (22 diffs)
-
trunk/admin/class-moderation-api-network-admin.php (added)
-
trunk/admin/css/moderation-api-admin.css (modified) (6 diffs)
-
trunk/admin/views/config.php (modified) (6 diffs)
-
trunk/admin/views/network (added)
-
trunk/admin/views/network/activate.php (added)
-
trunk/admin/views/network/config.php (added)
-
trunk/admin/views/network/get.php (added)
-
trunk/admin/views/network/how-it-works.php (added)
-
trunk/admin/views/network/start.php (added)
-
trunk/development.md (modified) (1 diff)
-
trunk/includes/class-moderation-api-activator.php (modified) (1 diff)
-
trunk/includes/class-moderation-api.php (modified) (17 diffs)
-
trunk/moderation-api.php (modified) (3 diffs)
-
trunk/public/class-moderation-api-public.php (modified) (12 diffs)
-
trunk/readme.txt (deleted)
-
trunk/release-plugin.sh (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
moderation-api-automated-content-moderation/tags/1.0.3/admin/class-moderation-api-admin.php
r3161458 r3326772 21 21 * @author Moderation API <[email protected]> 22 22 */ 23 class Moderation_Api_Admin { 23 class Moderation_Api_Admin 24 { 24 25 25 26 const NONCE = 'modapi-update-key'; … … 51 52 * @param string $version The version of this plugin. 52 53 */ 53 public function __construct( $plugin_name, $version ) { 54 public function __construct($plugin_name, $version) 55 { 54 56 55 57 $this->plugin_name = $plugin_name; … … 57 59 58 60 } 59 60 61 62 61 63 /** 62 64 * Register the stylesheets for the admin area. … … 64 66 * @since 1.0.0 65 67 */ 66 public function enqueue_styles() { 68 public function enqueue_styles() 69 { 67 70 68 71 /** … … 78 81 */ 79 82 80 wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/moderation-api-admin.css', array(), $this->version, 'all');83 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/moderation-api-admin.css', array(), $this->version, 'all'); 81 84 82 85 } … … 87 90 * @since 1.0.0 88 91 */ 89 public function enqueue_scripts() { 92 public function enqueue_scripts() 93 { 90 94 91 95 /** … … 101 105 */ 102 106 103 wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/moderation-api-admin.js', array( 'jquery' ), $this->version, false ); 104 } 105 106 function check_nonce(){ 107 wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'js/moderation-api-admin.js', array('jquery'), $this->version, false); 108 } 109 110 function check_nonce() 111 { 107 112 // Verify nonce for security 108 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) , Moderation_Api_Admin::NONCE)) { 109 wp_nonce_ays("error"); 110 } 111 } 112 113 public function init() { 113 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), Moderation_Api_Admin::NONCE)) { 114 wp_nonce_ays("error"); 115 } 116 } 117 118 public function init() 119 { 114 120 // check page is moderation-api 115 121 if (!isset($_GET['page']) || $_GET['page'] !== 'moderation-api') { … … 133 139 } 134 140 135 private function handle_post_request(){ 141 private function handle_post_request() 142 { 136 143 $this->check_nonce(); 137 144 $action = $this->get_action(); … … 139 146 } 140 147 141 private function handle_get_request(){ 148 private function handle_get_request() 149 { 142 150 $action = $this->get_action(); 143 151 $this->handle_action($action); 144 152 } 145 153 146 private function get_action(){ 154 private function get_action() 155 { 147 156 // get action from post or get 148 157 $action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : null; … … 150 159 $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : null; 151 160 } 152 if (!$action && isset($_GET['token'])){161 if (!$action && isset($_GET['token'])) { 153 162 $action = 'enter-key'; 154 163 } … … 157 166 158 167 159 private function handle_action($action){ 168 private function handle_action($action) 169 { 160 170 if ($action === 'enter-key') { 161 171 $api_key = sanitize_text_field($_POST['key'] ?? $_GET['token']); … … 165 175 $this->disconnect_key(); 166 176 } 167 } 168 169 170 private function save_key($api_key){ 177 if ($action === 'toggle-site-moderation') { 178 $this->toggle_site_moderation(); 179 } 180 if ($action === 'update-flagged-action') { 181 $this->save_flagged_action(); 182 } 183 if ($action === 'use-network-api-key') { 184 $this->use_network_api_key(); 185 } 186 if ($action === 'use-network-flagged-action') { 187 $this->use_network_flagged_action(); 188 } 189 } 190 191 192 private function save_key($api_key) 193 { 171 194 if (!$api_key) { 172 195 return; … … 176 199 177 200 // invalid key show notice 178 add_action('admin_notices', function () {201 add_action('admin_notices', function () { 179 202 ?> 180 203 <div class="notice notice-error is-dismissible"> … … 186 209 } 187 210 188 foreach (array('modapi_flagged_action') as $option) {189 // Sanitize the input190 $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3';191 192 // Escape the output before saving it to the database193 $escaped_value = esc_attr($input_value);194 195 // Update the option in the database196 update_option($option, $escaped_value);197 }198 199 211 $existingKey = Moderation_Api::get_api_key(); 200 201 update_option('moderation_api_key', $api_key); 202 203 if(!$existingKey){ 204 $this->sync_actions(); 205 } 206 207 } 208 209 210 private function disconnect_key() { 212 213 update_option('moderation_api_key', $api_key); 214 215 if (!$existingKey) { 216 $this->sync_actions(); 217 } 218 219 } 220 221 private function save_flagged_action() 222 { 223 foreach (array('modapi_flagged_action') as $option) { 224 // Sanitize the input 225 $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3'; 226 227 // Escape the output before saving it to the database 228 $escaped_value = esc_attr($input_value); 229 230 // Update the option in the database 231 update_option($option, $escaped_value); 232 } 233 234 // Show success notice 235 add_action('admin_notices', function () { 236 ?> 237 <div class="notice notice-success is-dismissible"> 238 <p><?php esc_html_e('Flagged comments setting saved successfully.', 'modapi'); ?></p> 239 </div> 240 <?php 241 }); 242 } 243 244 private function use_network_api_key() 245 { 246 // Remove the site-specific API key to fall back to network key 247 update_option('moderation_api_key', ''); 248 249 // Show success notice 250 add_action('admin_notices', function () { 251 ?> 252 <div class="notice notice-success is-dismissible"> 253 <p><?php esc_html_e('Switched to network API key successfully.', 'modapi'); ?></p> 254 </div> 255 <?php 256 }); 257 } 258 259 private function use_network_flagged_action() 260 { 261 // Remove the site-specific flagged action to fall back to network setting 262 update_option('modapi_flagged_action', ''); 263 264 // Show success notice 265 add_action('admin_notices', function () { 266 ?> 267 <div class="notice notice-success is-dismissible"> 268 <p><?php esc_html_e('Switched to network flagged comment setting successfully.', 'modapi'); ?></p> 269 </div> 270 <?php 271 }); 272 } 273 274 275 private function disconnect_key() 276 { 211 277 // remove the api key 212 278 update_option('moderation_api_key', ''); 213 279 } 214 280 215 function get_account($key) { 281 private function toggle_site_moderation() 282 { 283 // Check if network settings are enforced 284 if (is_multisite() && get_site_option('modapi_enforce_network', '0') === '1') { 285 // Don't allow toggling when network is enforced 286 add_action('admin_notices', function () { 287 ?> 288 <div class="notice notice-error is-dismissible"> 289 <p><?php esc_html_e('Cannot disable moderation: network settings are enforced.', 'moderation-api'); ?></p> 290 </div> 291 <?php 292 }); 293 return; 294 } 295 296 $current_state = get_option('modapi_site_disabled', '0'); 297 $new_state = ($current_state === '1') ? '0' : '1'; 298 299 update_option('modapi_site_disabled', $new_state); 300 301 // Show success message 302 $message = ($new_state === '1') 303 ? __('Site moderation has been disabled.', 'moderation-api') 304 : __('Site moderation has been enabled.', 'moderation-api'); 305 306 add_action('admin_notices', function () use ($message) { 307 ?> 308 <div class="notice notice-success is-dismissible"> 309 <p><?php echo esc_html($message); ?></p> 310 </div> 311 <?php 312 }); 313 } 314 315 function get_account($key) 316 { 216 317 // include key as header bearer 217 318 $headers = array( … … 220 321 ); 221 322 $url = Moderation_Api::API_URL . "/api/v1/account"; 222 323 223 324 224 325 $response = wp_remote_get($url, array( … … 237 338 238 339 239 function validate_api_key($key) { 340 function validate_api_key($key) 341 { 240 342 241 343 if (!$key) { … … 243 345 } 244 346 try { 245 347 246 348 $account = $this->get_account($key); 247 349 if ($account && $account->id) { … … 258 360 259 361 260 public function add_admin_menu() { 362 public function add_admin_menu() 363 { 261 364 add_options_page('Moderation API', 'Moderation API', 'manage_options', 'moderation-api', array($this, 'display_page')); 262 365 } 263 366 264 public function display_page() { 367 public function display_page() 368 { 265 369 $api_key = Moderation_Api::get_api_key(); 266 370 267 371 if ($api_key) { 268 $account = $this->get_account($api_key); 269 if (!$account) { 270 $this->disconnect_key(); 271 return; 272 } 273 274 Moderation_Api::view('config', array('modapi_user' => $account)); 372 $account = $this->get_account($api_key); 373 if (!$account) { 374 $this->disconnect_key(); 275 375 return; 376 } 377 378 Moderation_Api::view('config', array('modapi_user' => $account)); 379 return; 276 380 } 277 381 … … 284 388 285 389 286 public static function get_page_url($page = 'config') { 390 public static function get_page_url($page = 'config') 391 { 287 392 288 393 $args = array('page' => 'moderation-api'); … … 298 403 return add_query_arg($args, menu_page_url('moderation-api', false)); 299 404 } 300 405 301 406 302 407 // Add a custom column to the comments list table 303 function custom_comments_column($columns) { 408 function custom_comments_column($columns) 409 { 304 410 // Add a new column 305 411 $columns['custom_flagged'] = 'Flagged'; … … 308 414 309 415 // Display the flagged value in the custom column 310 function show_flagged_custom_field($column_name, $comment_id) { 416 function show_flagged_custom_field($column_name, $comment_id) 417 { 311 418 if ($column_name === 'custom_flagged') { 312 $flagged = get_comment_meta($comment_id, 'modapi_flagged', true); 313 if($flagged){ 314 echo '<span style="color: red;">' . __('Flagged', 'moderation-api') . '</span>'; 315 }else{ 316 echo '<span style="color: green;">' . __('Not Flagged', 'moderation-api') . '</span>'; 317 } 318 } 319 } 320 321 322 function add_modapi_error_column($columns) { 323 $columns['modapi_error'] = __('Moderation API error', 'moderation-api'); 324 return $columns; 325 } 326 327 328 function populate_modapi_error_column($column, $comment_ID) { 329 if ('modapi_error' === $column) { 330 $modapi_error = get_comment_meta($comment_ID, 'modapi_error', true); 331 if ($modapi_error) { 332 echo esc_html($modapi_error); 333 } else { 334 echo __('', 'moderation-api'); 335 } 419 $flagged = get_comment_meta($comment_id, 'modapi_flagged', true); 420 if ($flagged) { 421 echo '<span style="color: red;">' . __('Flagged', 'moderation-api') . '</span>'; 422 } else { 423 echo '<span style="color: green;">' . __('Not Flagged', 'moderation-api') . '</span>'; 336 424 } 337 } 338 339 340 private function sync_actions() { 425 } 426 } 427 428 429 function add_modapi_error_column($columns) 430 { 431 $columns['modapi_error'] = __('Moderation API error', 'moderation-api'); 432 return $columns; 433 } 434 435 436 function populate_modapi_error_column($column, $comment_ID) 437 { 438 if ('modapi_error' === $column) { 439 $modapi_error = get_comment_meta($comment_ID, 'modapi_error', true); 440 if ($modapi_error) { 441 echo esc_html($modapi_error); 442 } else { 443 echo __('', 'moderation-api'); 444 } 445 } 446 } 447 448 449 private function sync_actions() 450 { 341 451 342 452 $apiKey = Moderation_Api::get_api_key(); … … 355 465 ) 356 466 ) 357 ),467 ), 358 468 array( 359 469 'name' => 'Show on WP', -
moderation-api-automated-content-moderation/tags/1.0.3/admin/css/moderation-api-admin.css
r3177384 r3326772 466 466 } 467 467 468 .modapi-card .modapi-card-actions {469 margin-top: 1rem;470 }471 472 468 .jetpack_page_modapi-key-config .update-nag, 473 469 .settings_page_moderation-api .update-nag { … … 537 533 .modapi-box-header { 538 534 max-width: 700px; 539 margin: 0 auto 40px auto;535 margin: 0 auto 0px auto; 540 536 line-height: 1.5; 541 537 } … … 627 623 } 628 624 629 .modapi-button.secondary {625 #modapi-plugin-container .modapi-button.secondary { 630 626 background: #f5f5f5; 631 627 border-color: #c8d7e1; 632 628 color: #2e4453; 633 629 } 634 .modapi-button.secondary:hover {630 #modapi-plugin-container .modapi-button.secondary:hover { 635 631 background: #e6ecf1; 636 632 border-color: #a8bece; … … 886 882 } 887 883 884 .modapi-settings__row-input .description { 885 margin-top: 0.5em; 886 } 887 888 888 .modapi-settings__row-title { 889 889 font-weight: 500; … … 891 891 margin: 0; 892 892 margin-bottom: 1em; 893 align-items: center; 893 894 } 894 895 … … 899 900 .modapi-card-actions { 900 901 padding: 1em; 902 display: flex; 903 justify-content: space-between; 904 align-items: center; 905 } 906 907 .modapi-card-actions-extra { 908 position: absolute; 909 left: 1em; 910 bottom: 1em; 911 display: flex; 912 align-items: center; 901 913 } 902 914 903 915 .modapi-settings__row label { 904 916 padding-bottom: 1em; 917 } 918 .modapi-settings__row-title label { 919 padding-bottom: 0 !important; 905 920 } 906 921 -
moderation-api-automated-content-moderation/tags/1.0.3/admin/views/config.php
r3143763 r3326772 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 2 if (!defined('ABSPATH')) 3 exit; // Exit if accessed directly 3 4 4 5 //phpcs:disable VariableAnalysis … … 9 10 <div class="modapi-masthead"> 10 11 <div class="modapi-masthead__inside-container"> 11 <?php Moderation_Api::view( 'logo'); ?>12 <?php Moderation_Api::view('logo'); ?> 12 13 </div> 13 14 </div> 14 15 <div class="modapi-lower"> 15 <?php if ( Moderation_Api::get_api_key()) { ?>16 <?php if (Moderation_Api::get_api_key()) { ?> 16 17 <?php Moderation_Api::display_status(); ?> 17 18 <?php } ?> 18 <?php if ( ! empty( $notices )) { ?>19 <?php foreach ( $notices as $notice) { ?>20 <?php Moderation_Api::view( 'notice', $notice); ?>19 <?php if (!empty($notices)) { ?> 20 <?php foreach ($notices as $notice) { ?> 21 <?php Moderation_Api::view('notice', $notice); ?> 21 22 <?php } ?> 22 23 <?php } ?> 23 24 24 25 26 <div class="modapi-card"> 27 <div class="modapi-section-header"> 28 <h2 class="modapi-section-header__label"> 29 <span><?php esc_html_e( 'Account' , 'modapi'); ?></span> 30 </h2> 25 26 27 <div class="modapi-card"> 28 <div class="modapi-section-header"> 29 <h2 class="modapi-section-header__label"> 30 <span><?php esc_html_e('Account', 'modapi'); ?></span> 31 </h2> 32 </div> 33 34 <div class="inside"> 35 <table class="modapi-account"> 36 <tbody> 37 <tr> 38 <th scope="row"><?php esc_html_e('Project', 'modapi'); ?></th> 39 <td> 40 <?php echo esc_html($modapi_user->current_project->name); ?> 41 </td> 42 </tr> 43 44 <tr> 45 <th scope="row"><?php esc_html_e('Subscription type', 'modapi'); ?></th> 46 <td> 47 <?php echo esc_html($modapi_user->paid_plan_name); ?> 48 </td> 49 </tr> 50 51 52 <tr> 53 <th scope="row"><?php esc_html_e('Quota', 'modapi'); ?></th> 54 <td> 55 <?php echo number_format(esc_html($modapi_user->text_api_quota)); ?> 56 </td> 57 </tr> 58 59 <tr> 60 <th scope="row"><?php esc_html_e('Remaining quota', 'modapi'); ?></th> 61 <td> 62 <?php echo number_format(esc_html($modapi_user->remaining_quota)); ?> 63 </td> 64 </tr> 65 66 67 </tbody> 68 </table> 69 <div class="modapi-settings__row"></div> 70 <div class="modapi-card-actions"> 71 <div id="delete-action"> 72 <a class="submitdelete deletion" 73 href="<?php echo esc_url(Moderation_Api_Admin::get_page_url('delete_key')); ?>"><?php esc_html_e('Disconnect this account', 'modapi'); ?></a> 31 74 </div> 32 75 33 <div class="inside"> 34 <table class="modapi-account"> 35 <tbody> 36 <tr> 37 <th scope="row"><?php esc_html_e( 'Project', 'modapi' ); ?></th> 38 <td> 39 <?php echo esc_html( $modapi_user->current_project->name ); ?> 40 </td> 41 </tr> 42 43 <tr> 44 <th scope="row"><?php esc_html_e( 'Subscription type', 'modapi' ); ?></th> 45 <td> 46 <?php echo esc_html( $modapi_user->paid_plan_name ); ?> 47 </td> 48 </tr> 49 50 51 <tr> 52 <th scope="row"><?php esc_html_e( 'Quota', 'modapi' ); ?></th> 53 <td> 54 <?php echo number_format(esc_html( $modapi_user->text_api_quota )); ?> 55 </td> 56 </tr> 57 58 <tr> 59 <th scope="row"><?php esc_html_e( 'Remaining quota', 'modapi' ); ?></th> 60 <td> 61 <?php echo number_format(esc_html( $modapi_user->remaining_quota )); ?> 62 </td> 63 </tr> 64 65 66 </tbody> 67 </table> 68 <div class="modapi-settings__row"></div> 69 <div class="modapi-card-actions"> 70 <div id="delete-action"> 71 <a class="submitdelete deletion" href="<?php echo esc_url( Moderation_Api_Admin::get_page_url( 'delete_key' ) ); ?>"><?php esc_html_e( 'Disconnect this account', 'modapi' ); ?></a> 72 </div> 73 74 <div id="publishing-action"> 75 <a href="https://moderationapi.com/app/upgrade" target="_blank"> 76 <button type="button" class="modapi-button " > 77 <?php esc_attr_e( 'Upgrade plan', 'modapi' ); ?> 78 </button> 79 </a> 80 </div> 81 <div class="clear"></div> 82 </div> 76 <div id="publishing-action"> 77 <a href="https://moderationapi.com/app/upgrade" target="_blank"> 78 <button type="button" class="modapi-button "> 79 <?php esc_attr_e('Upgrade plan', 'modapi'); ?> 80 </button> 81 </a> 83 82 </div> 84 83 </div> 85 84 </div> 85 </div> 86 87 <?php if (is_multisite()): ?> 88 <?php 89 $network_api_key = get_site_option('moderation_api_network_key'); 90 $enforce_network = get_site_option('modapi_enforce_network', '0'); 91 $can_override = Moderation_Api_Network_Admin::can_override_network_settings(); 92 $using_network_key = !get_option('moderation_api_key') && $network_api_key; 93 ?> 86 94 <div class="modapi-card"> 87 95 <div class="modapi-section-header"> 88 96 <h2 class="modapi-section-header__label"> 89 <span><?php esc_html_e( 'Settings', 'modapi'); ?></span>97 <span><?php esc_html_e('Multisite Settings', 'modapi'); ?></span> 90 98 </h2> 91 99 </div> 92 100 93 101 <div class="inside"> 94 <form action="<?php echo esc_url( Moderation_Api_Admin::get_page_url() ); ?>" autocomplete="off" method="POST" id="modapi-settings-form"> 95 102 <div class="modapi-settings"> 103 104 105 <div class="modapi-settings__row"> 106 <h3 class="modapi-settings__row-title"> 107 <label 108 class="modapi-settings__row-label"><?php esc_html_e('Site Moderation', 'modapi'); ?></label> 109 </h3> 110 <div style="margin-bottom: 10px;"> 111 <?php 112 $site_disabled = get_option('modapi_site_disabled', '0'); 113 $network_enforced = $enforce_network === '1'; 114 ?> 115 <?php if ($network_enforced): ?> 116 <span 117 style="background: #646970; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 118 <?php esc_html_e('NETWORK CONTROLLED', 'modapi'); ?> 119 </span> 120 <p style="color: #646970; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 121 <?php esc_html_e('Moderation cannot be disabled when network settings are enforced.', 'modapi'); ?> 122 </p> 123 <?php else: ?> 124 <?php if ($site_disabled === '1'): ?> 125 <span 126 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 127 <?php esc_html_e('MODERATION DISABLED', 'modapi'); ?> 128 </span> 129 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 130 <?php esc_html_e('Comments will not be analyzed by Moderation API on this site.', 'modapi'); ?> 131 </p> 132 <?php else: ?> 133 134 <p style="font-size: 13px; margin: 5px 0 0 0; "> 135 <?php esc_html_e('Disable moderation for this site only.', 'modapi'); ?> 136 </p> 137 <?php endif; ?> 138 <?php endif; ?> 139 </div> 140 <div class="modapi-settings__row-input"> 141 <?php if (!$network_enforced): ?> 142 <form method="post" style="display: flex; flex-direction: column; align-items: flex-start;"> 143 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 144 <input type="hidden" name="action" value="toggle-site-moderation"> 145 <?php if ($site_disabled === '1'): ?> 146 <input type="submit" name="submit" 147 value="<?php esc_attr_e('Enable Moderation', 'modapi'); ?>" 148 class=" modapi-button secondary"> 149 <?php else: ?> 150 <input type="submit" name="submit" 151 value="<?php esc_attr_e('Disable Moderation For This Site', 'modapi'); ?>" 152 class=" modapi-button secondary"> 153 <?php endif; ?> 154 </form> 155 <?php endif; ?> 156 </div> 157 </div> 158 159 <?php if (current_user_can('manage_network_options')): ?> 160 <div class="modapi-settings__row"> 161 <h3 class="modapi-settings__row-title"> 162 <label 163 class="modapi-settings__row-label"><?php esc_html_e('Network Admin', 'modapi'); ?></label> 164 </h3> 165 <div class="modapi-settings__row-input"> 166 <a href="<?php echo esc_url(network_admin_url('settings.php?page=moderation-api-network')); ?>" 167 class=" modapi-button secondary"> 168 <?php esc_html_e('Manage Network Settings', 'modapi'); ?> 169 </a> 170 </div> 171 </div> 172 <?php endif; ?> 173 </div> 174 </div> 175 </div> 176 <?php endif; ?> 177 178 <?php 179 // Ensure $site_disabled is always defined 180 if (!isset($site_disabled)) { 181 $site_disabled = get_option('modapi_site_disabled', '0'); 182 } 183 ?> 184 185 <?php if ($site_disabled !== '1'): ?> 186 187 <div class="modapi-card"> 188 <div class="modapi-section-header"> 189 <h2 class="modapi-section-header__label"> 190 <span><?php esc_html_e('API Configuration', 'modapi'); ?></span> 191 </h2> 192 </div> 193 194 <div class="inside"> 195 <form action="<?php echo esc_url(Moderation_Api_Admin::get_page_url()); ?>" autocomplete="off" 196 method="POST" id="modapi-settings-form"> 197 96 198 <div class="modapi-settings"> 97 <div class="modapi-settings__row"> 199 <div class="modapi-settings__row"> 200 <?php 201 $site_api_key = get_option('moderation_api_key'); 202 $network_api_key = get_site_option('moderation_api_network_key'); 203 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 204 $using_site_key = !empty($site_api_key) && !$network_enforced; 205 $using_network_key = !$using_site_key && !empty($network_api_key); 206 ?> 207 <h3 class="modapi-settings__row-title"> 208 <label class="modapi-settings__row-label" 209 for="key"><?php esc_html_e('API key', 'modapi'); ?></label> 210 </h3> 211 <?php if (is_multisite() && ($using_site_key || $using_network_key || $network_enforced)): ?> 212 <div style="margin-bottom: 10px;"> 213 <?php if ($network_enforced): ?> 214 <span 215 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 216 <?php esc_html_e('NETWORK ENFORCED', 'modapi'); ?> 217 </span> 218 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 219 <?php esc_html_e('This API key is enforced by network settings and cannot be changed at the site level.', 'modapi'); ?> 220 </p> 221 <?php elseif ($using_site_key): ?> 222 <span 223 style="background: #dba617; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 224 <?php esc_html_e('SITE OVERRIDE', 'modapi'); ?> 225 </span> 226 <p style="color: #dba617; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 227 <?php esc_html_e('This site is using a custom API key that overrides the network default.', 'modapi'); ?> 228 </p> 229 <?php elseif ($using_network_key): ?> 230 <span 231 style="background: #00a32a; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 232 <?php esc_html_e('NETWORK DEFAULT', 'modapi'); ?> 233 </span> 234 <p style="color: #00a32a; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 235 <?php esc_html_e('This site is using the network-wide API key.', 'modapi'); ?> 236 </p> 237 <?php endif; ?> 238 </div> 239 <?php endif; ?> 240 <div class="modapi-settings__row-input"> 241 <?php 242 $site_api_key = get_option('moderation_api_key'); 243 $network_api_key = get_site_option('moderation_api_network_key'); 244 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 245 $using_site_key = !empty($site_api_key) && !$network_enforced; 246 $using_network_key = !$using_site_key && !empty($network_api_key); 247 248 if ($network_enforced) { 249 $api_key_value = $network_api_key; 250 } else if ($using_site_key) { 251 $api_key_value = $site_api_key; 252 } else { 253 $api_key_value = $network_api_key ?: ''; 254 } 255 ?> 256 257 <span class="api-key"> 258 <input id="key" name="key" type="text" style="width: 100%;" 259 value="<?php echo esc_attr($api_key_value); ?>" <?php echo $network_enforced ? 'readonly disabled' : ''; ?>> 260 </span> 261 262 263 </div> 264 </div> 265 266 </div> 267 268 <?php if (!$network_enforced): ?> 269 <div class="modapi-card-actions"> 270 271 272 273 <div style="flex: 1;"></div> 274 275 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 276 277 <div id="publishing-action"> 278 <input type="hidden" name="action" value="enter-key"> 279 <input type="submit" name="submit" id="submit" class="modapi-button modapi-could-be-primary" 280 value="<?php esc_attr_e('Save API Key', 'modapi'); ?>"> 281 </div> 282 </div> 283 <?php endif; ?> 284 </form> 285 286 287 <?php if ($using_site_key && !empty($network_api_key)): ?> 288 <div class="modapi-card-actions-extra"> 289 <form method="post" style="margin-top: 10px;"> 290 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 291 <input type="hidden" name="action" value="use-network-api-key"> 292 <input type="submit" name="submit" value="<?php esc_attr_e('Use Network API Key', 'modapi'); ?>" 293 class="modapi-button secondary" style="font-size: 12px; padding: 4px 8px;"> 294 </form> 295 </div> 296 <?php endif; ?> 297 298 299 </div> 300 </div> 301 302 <div class="modapi-card"> 303 <div class="modapi-section-header"> 304 <h2 class="modapi-section-header__label"> 305 <span><?php esc_html_e('Comment Moderation', 'modapi'); ?></span> 306 </h2> 307 </div> 308 309 <div class="inside"> 310 <div class="modapi-settings"> 311 <div class="modapi-settings__row"> 312 <h3 class="modapi-settings__row-title"> 313 <label 314 class="modapi-settings__row-label"><?php esc_html_e('Content filter', 'modapi'); ?></label> 315 </h3> 316 <div class="modapi-settings__row-input"> 317 <a href="<?php echo esc_url(Moderation_Api::API_URL . '/app/projects/' . esc_attr($modapi_user->current_project->id)); ?>" 318 target="_blank"> 319 <button type="button" class="modapi-button secondary"> 320 <?php esc_attr_e('Edit content filter', 'modapi'); ?> 321 </button> 322 </a> 323 </div> 324 </div> 325 </div> 326 327 <form action="<?php echo esc_url(Moderation_Api_Admin::get_page_url()); ?>" autocomplete="off" 328 method="POST" id="modapi-flagged-comments-form"> 329 <div class="modapi-settings"> 330 <div class="modapi-settings__row is-radio"> 331 <?php 332 $site_flagged_action = get_option('modapi_flagged_action'); 333 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 334 $effective_action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action'); 335 $radio_disabled = $network_enforced ? 'disabled' : ''; 336 337 // More precise logic for determining setting source 338 $has_site_flagged = ($site_flagged_action !== false && $site_flagged_action !== ''); 339 $has_network_flagged = ($network_flagged_action !== false && $network_flagged_action !== ''); 340 $using_site_flagged = $has_site_flagged && !$network_enforced; 341 $using_network_flagged = !$has_site_flagged && $has_network_flagged && !$network_enforced; 342 343 // Debug info (remove in production) 344 if (WP_DEBUG) { 345 echo "<!-- DEBUG: site_flagged_action: " . var_export($site_flagged_action, true) . " -->"; 346 echo "<!-- DEBUG: network_flagged_action: " . var_export($network_flagged_action, true) . " -->"; 347 echo "<!-- DEBUG: has_site_flagged: " . var_export($has_site_flagged, true) . " -->"; 348 echo "<!-- DEBUG: has_network_flagged: " . var_export($has_network_flagged, true) . " -->"; 349 echo "<!-- DEBUG: using_site_flagged: " . var_export($using_site_flagged, true) . " -->"; 350 echo "<!-- DEBUG: network_enforced: " . var_export($network_enforced, true) . " -->"; 351 } 352 ?> 353 <div class="modapi-settings__row-text"> 98 354 <h3 class="modapi-settings__row-title"> 99 < label class="modapi-settings__row-label" for="key"><?php esc_html_e( 'API key', 'modapi' ); ?></label>355 <?php esc_html_e('Flagged comments', 'modapi'); ?> 100 356 </h3> 101 <div class="modapi-settings__row-input"> 102 <span class="api-key"><input id="key" name="key" type="text" style="width: 100%;" value="<?php echo esc_attr( get_option('moderation_api_key') ); ?>" ></span> 103 </div> 104 </div> 105 106 107 <div class="modapi-settings__row"> 108 <h3 class="modapi-settings__row-title"> 109 <label class="modapi-settings__row-label" for="key"><?php esc_html_e( 'Content filter', 'modapi' ); ?></label> 110 </h3> 111 <div class="modapi-settings__row-input"> 112 <a href="<?php echo esc_url(Moderation_Api::API_URL . '/app/projects/' . esc_attr($modapi_user->current_project->id)); ?>" target="_blank"> 113 <button type="button" class="modapi-button secondary" > 114 <?php esc_attr_e( 'Edit content filter', 'modapi' ); ?> 115 </button> 116 </a> 117 </div> 118 119 </div> 120 121 122 123 <div class="modapi-settings__row is-radio"> 124 <div class="modapi-settings__row-text"> 125 <h3 class="modapi-settings__row-title"><?php esc_html_e( 'Flagged comments', 'modapi' ); ?></h3> 357 <?php if (is_multisite() && ($using_site_flagged || $using_network_flagged || $network_enforced)): ?> 358 <div style="margin-bottom: 15px;"> 359 <?php if ($network_enforced): ?> 360 <span 361 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 362 <?php esc_html_e('NETWORK ENFORCED', 'modapi'); ?> 363 </span> 364 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 365 <?php esc_html_e('These settings are enforced by network configuration and cannot be changed at the site level.', 'modapi'); ?> 366 </p> 367 <?php elseif ($using_site_flagged): ?> 368 <span 369 style="background: #dba617; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 370 <?php esc_html_e('SITE OVERRIDE', 'modapi'); ?> 371 </span> 372 <p style="color: #dba617; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 373 <?php esc_html_e('This site is using custom flagged comment behavior that overrides the network default.', 'modapi'); ?> 374 </p> 375 <?php elseif ($using_network_flagged): ?> 376 <span 377 style="background: #00a32a; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 378 <?php esc_html_e('NETWORK DEFAULT', 'modapi'); ?> 379 </span> 380 <p style="color: #00a32a; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 381 <?php esc_html_e('This site is using the network-wide flagged comment behavior.', 'modapi'); ?> 382 </p> 383 <?php endif; ?> 384 </div> 385 <?php endif; ?> 126 386 </div> 127 387 <div class="modapi-settings__row-input"> 128 <fieldset> 129 388 <?php 389 $site_flagged_action = get_option('modapi_flagged_action'); 390 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 391 $effective_action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action'); 392 $radio_disabled = $network_enforced ? 'disabled' : ''; 393 394 // More precise logic for determining setting source 395 $has_site_flagged = ($site_flagged_action !== false && $site_flagged_action !== ''); 396 $has_network_flagged = ($network_flagged_action !== false && $network_flagged_action !== ''); 397 $using_site_flagged = $has_site_flagged && !$network_enforced; 398 $using_network_flagged = !$has_site_flagged && $has_network_flagged && !$network_enforced; 399 ?> 400 <fieldset <?php echo $network_enforced ? 'disabled' : ''; ?>> 401 130 402 <div> 131 403 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_3"> 132 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_3" value="3" <?php checked( '3', get_option( 'modapi_flagged_action' ) ); ?> /> 404 <input type="radio" name="modapi_flagged_action" 405 id="modapi_flagged_action_3" value="3" <?php checked('3', $effective_action); ?> <?php echo $radio_disabled; ?> /> 133 406 <span class="modapi-settings__row-label-text"> 134 <?php esc_html_e( 'Move flagged comments to spam.', 'modapi'); ?>407 <?php esc_html_e('Move flagged comments to spam.', 'modapi'); ?> 135 408 </span> 136 409 </label> … … 138 411 <div> 139 412 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_2"> 140 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_2" value="2" <?php checked( '2', get_option( 'modapi_flagged_action' ) ); ?> /> 413 <input type="radio" name="modapi_flagged_action" 414 id="modapi_flagged_action_2" value="2" <?php checked('2', $effective_action); ?> <?php echo $radio_disabled; ?> /> 141 415 <span class="modapi-settings__row-label-text"> 142 <?php esc_html_e( 'Move flagged comments to pending for review.', 'modapi'); ?>416 <?php esc_html_e('Move flagged comments to pending for review.', 'modapi'); ?> 143 417 </span> 144 418 </label> … … 147 421 <div> 148 422 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_4"> 149 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_4" value="4" <?php checked( '4', get_option( 'modapi_flagged_action' ) ); ?> /> 423 <input type="radio" name="modapi_flagged_action" 424 id="modapi_flagged_action_4" value="4" <?php checked('4', $effective_action); ?> <?php echo $radio_disabled; ?> /> 150 425 <span class="modapi-settings__row-label-text"> 151 <?php esc_html_e( 'Move flagged comments to trash.', 'modapi'); ?>426 <?php esc_html_e('Move flagged comments to trash.', 'modapi'); ?> 152 427 </span> 153 428 </label> 154 429 </div> 155 430 156 431 <div> 157 432 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_1"> 158 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_1" value="1" <?php checked( '1', get_option( 'modapi_flagged_action' ) ); ?> /> 433 <input type="radio" name="modapi_flagged_action" 434 id="modapi_flagged_action_1" value="1" <?php checked('1', $effective_action); ?> <?php echo $radio_disabled; ?> /> 159 435 <span class="modapi-settings__row-label-text"> 160 <?php esc_html_e( 'Move flagged comments to approved.', 'modapi'); ?>436 <?php esc_html_e('Move flagged comments to approved.', 'modapi'); ?> 161 437 </span> 162 438 </label> … … 164 440 <div> 165 441 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_0"> 166 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_0" value="0" <?php checked( '0', get_option( 'modapi_flagged_action' ) ); ?> /> 442 <input type="radio" name="modapi_flagged_action" 443 id="modapi_flagged_action_0" value="0" <?php checked('0', $effective_action); ?> <?php echo $radio_disabled; ?> /> 167 444 <span class="modapi-settings__row-label-text"> 168 <?php esc_html_e( 'Do not do anything.', 'modapi'); ?>445 <?php esc_html_e('Do not do anything.', 'modapi'); ?> 169 446 </span> 170 447 </label> … … 174 451 175 452 <div class="modapi-settings__row-note"> 176 <strong><?php esc_html_e( 'Note:', 'modapi' ); ?></strong> 177 We recommend to use the <a href="https://moderationapi.com/app/moderation/queue" target="_blank">content queue</a> in the Moderation API dashboard to review flagged comments. 453 <strong><?php esc_html_e('Note:', 'modapi'); ?></strong> 454 We recommend to use the <a href="https://moderationapi.com/app/moderation/queue" 455 target="_blank">content queue</a> in the Moderation API dashboard to review 456 flagged comments. 178 457 </div> 179 458 180 459 </div> 181 460 </div> 182 183 184 461 </div> 185 186 <div class="modapi-card-actions"> 187 188 189 <?php wp_nonce_field( Moderation_Api_Admin::NONCE ); ?> 190 191 <div id="publishing-action"> 192 <input type="hidden" name="action" value="enter-key"> 193 <input type="submit" name="submit" id="submit" class="modapi-button modapi-could-be-primary" value="<?php esc_attr_e( 'Save changes', 'modapi' ); ?>"> 194 </div> 195 <div class="clear"></div> 462 463 <?php if (!$network_enforced): ?> 464 <div class="modapi-card-actions"> 465 466 467 <div style="flex: 1;"></div> 468 469 470 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 471 472 <div id="publishing-action"> 473 <input type="hidden" name="action" value="update-flagged-action"> 474 <input type="submit" name="submit" id="submit-flagged" 475 class="modapi-button modapi-could-be-primary" 476 value="<?php esc_attr_e('Save', 'modapi'); ?>"> 477 </div> 478 </div> 479 <?php endif; ?> 480 </form> 481 482 483 <?php if (is_multisite() && $using_site_flagged && $has_network_flagged && !$network_enforced): ?> 484 <div class="modapi-card-actions-extra"> 485 <form method="post" class=""> 486 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 487 <input type="hidden" name="action" value="use-network-flagged-action"> 488 <input type="submit" name="submit" value="<?php esc_attr_e('Use Network Setting', 'modapi'); ?>" 489 class="modapi-button secondary" style="font-size: 12px; padding: 4px 8px;"> 490 </form> 491 492 493 </form> 494 <span style="margin-left: 10px; color: #666; font-size: 12px;"> 495 <?php esc_html_e('Remove site override and use network default', 'modapi'); ?> 496 </span> 196 497 </div> 197 </form> 498 <?php endif; ?> 499 500 198 501 </div> 199 502 </div> 200 503 201 504 <?php endif; // End if site is not disabled ?> 505 202 506 </div> 203 507 </div> -
moderation-api-automated-content-moderation/tags/1.0.3/development.md
r3177384 r3326772 13 13 npx @wordpress/env start 14 14 ``` 15 16 ## Admin credentials 17 18 admin 19 password -
moderation-api-automated-content-moderation/tags/1.0.3/includes/class-moderation-api-activator.php
r3143763 r3326772 31 31 */ 32 32 public static function activate() { 33 // Initialize default flagged action setting if not already set 34 if (get_option('modapi_flagged_action') === false) { 35 add_option('modapi_flagged_action', '2'); 36 } 37 } 33 38 39 /** 40 * Network activation - Initialize default settings for new sites 41 * 42 * @since 1.0.3 43 */ 44 public static function network_activate() { 45 // Don't create local flagged action setting for new sites 46 // This allows them to inherit the network setting through get_effective_flagged_action() 47 48 // Only create the option if there's no network setting at all 49 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 50 if ($network_flagged_action === false || $network_flagged_action === '') { 51 // No network setting exists, create a local default 52 if (get_option('modapi_flagged_action') === false) { 53 add_option('modapi_flagged_action', '2'); 54 } 55 } 56 // Otherwise, leave no local option so the site inherits network settings 34 57 } 35 58 -
moderation-api-automated-content-moderation/tags/1.0.3/includes/class-moderation-api.php
r3177384 r3326772 28 28 * @author Moderation API <[email protected]> 29 29 */ 30 class Moderation_Api { 30 class Moderation_Api 31 { 31 32 32 33 /** … … 70 71 * @since 1.0.0 71 72 */ 72 public function __construct() { 73 if ( defined( 'MODERATION_API_VERSION' ) ) { 73 public function __construct() 74 { 75 if (defined('MODERATION_API_VERSION')) { 74 76 $this->version = MODERATION_API_VERSION; 75 77 } else { … … 83 85 $this->define_public_hooks(); 84 86 87 if (is_multisite()) { 88 $this->define_network_admin_hooks(); 89 } 90 85 91 } 86 92 … … 101 107 * @access private 102 108 */ 103 private function load_dependencies() { 109 private function load_dependencies() 110 { 104 111 105 112 /** … … 107 114 * core plugin. 108 115 */ 109 require_once plugin_dir_path( dirname( __FILE__ )) . 'includes/class-moderation-api-loader.php';116 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-moderation-api-loader.php'; 110 117 111 118 /** … … 113 120 * of the plugin. 114 121 */ 115 require_once plugin_dir_path( dirname( __FILE__ )) . 'includes/class-moderation-api-i18n.php';122 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-moderation-api-i18n.php'; 116 123 117 124 /** 118 125 * The class responsible for defining all actions that occur in the admin area. 119 126 */ 120 require_once plugin_dir_path( dirname( __FILE__ )) . 'admin/class-moderation-api-admin.php';127 require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-moderation-api-admin.php'; 121 128 122 129 /** … … 124 131 * side of the site. 125 132 */ 126 require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-moderation-api-public.php'; 133 require_once plugin_dir_path(dirname(__FILE__)) . 'public/class-moderation-api-public.php'; 134 135 /** 136 * The class responsible for defining network admin functionality in multisite. 137 */ 138 if (is_multisite()) { 139 require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-moderation-api-network-admin.php'; 140 } 127 141 128 142 $this->loader = new Moderation_Api_Loader(); … … 139 153 * @access private 140 154 */ 141 private function set_locale() { 155 private function set_locale() 156 { 142 157 143 158 $plugin_i18n = new Moderation_Api_i18n(); 144 159 145 $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');160 $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain'); 146 161 147 162 } … … 154 169 * @access private 155 170 */ 156 private function define_admin_hooks() { 157 158 $plugin_admin = new Moderation_Api_Admin( $this->get_plugin_name(), $this->get_version() ); 159 160 $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); 161 $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); 171 private function define_admin_hooks() 172 { 173 174 $plugin_admin = new Moderation_Api_Admin($this->get_plugin_name(), $this->get_version()); 175 176 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles'); 177 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts'); 162 178 163 179 // add admin sidepanel 164 $this->loader->add_action( 'admin_menu', $plugin_admin, 'add_admin_menu');180 $this->loader->add_action('admin_menu', $plugin_admin, 'add_admin_menu'); 165 181 166 182 // hook for admin settings page 167 $this->loader->add_action( 'admin_init', $plugin_admin, 'init');168 169 170 $this->loader->add_filter( 'manage_edit-comments_columns', $plugin_admin, 'custom_comments_column');171 $this->loader->add_action( 'manage_comments_custom_column', $plugin_admin, 'show_flagged_custom_field', 10, 2);183 $this->loader->add_action('admin_init', $plugin_admin, 'init'); 184 185 186 $this->loader->add_filter('manage_edit-comments_columns', $plugin_admin, 'custom_comments_column'); 187 $this->loader->add_action('manage_comments_custom_column', $plugin_admin, 'show_flagged_custom_field', 10, 2); 172 188 173 189 … … 177 193 // Add a custom column to the comments list table 178 194 $this->loader->add_filter('manage_edit-comments_columns', $plugin_admin, 'add_modapi_error_column'); 179 180 $this->loader->add_action( 'admin_notices', $this, 'add_admin_notices');195 196 $this->loader->add_action('admin_notices', $this, 'add_admin_notices'); 181 197 182 198 } … … 191 207 * @access private 192 208 */ 193 private function define_public_hooks() { 194 195 $plugin_public = new Moderation_Api_Public( $this->get_plugin_name(), $this->get_version() ); 196 197 $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); 198 $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); 209 private function define_public_hooks() 210 { 211 212 $plugin_public = new Moderation_Api_Public($this->get_plugin_name(), $this->get_version()); 213 214 $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_styles'); 215 $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_scripts'); 199 216 200 217 $this->loader->add_action('comment_post', $plugin_public, 'moderation_api_comment_post', 10, 2); … … 207 224 } 208 225 209 210 211 226 /** 227 * Register all of the hooks related to the network admin functionality 228 * of the plugin. 229 * 230 * @since 1.0.3 231 * @access private 232 */ 233 private function define_network_admin_hooks() 234 { 235 236 $plugin_network_admin = new Moderation_Api_Network_Admin($this->get_plugin_name(), $this->get_version()); 237 238 $this->loader->add_action('network_admin_enqueue_scripts', $plugin_network_admin, 'enqueue_styles'); 239 $this->loader->add_action('network_admin_enqueue_scripts', $plugin_network_admin, 'enqueue_scripts'); 240 241 // Add network admin menu 242 $this->loader->add_action('network_admin_menu', $plugin_network_admin, 'add_network_admin_menu'); 243 244 // Hook for network admin settings page processing 245 $this->loader->add_action('admin_init', $plugin_network_admin, 'init'); 246 $this->loader->add_action('network_admin_init', $plugin_network_admin, 'init'); 247 } 248 249 250 251 212 252 213 253 /** … … 216 256 * @since 1.0.0 217 257 */ 218 public function run() { 258 public function run() 259 { 219 260 $this->loader->run(); 220 261 } … … 227 268 * @return string The name of the plugin. 228 269 */ 229 public function get_plugin_name() { 270 public function get_plugin_name() 271 { 230 272 return $this->plugin_name; 231 273 } … … 237 279 * @return Moderation_Api_Loader Orchestrates the hooks of the plugin. 238 280 */ 239 public function get_loader() { 281 public function get_loader() 282 { 240 283 return $this->loader; 241 284 } … … 247 290 * @return string The version number of the plugin. 248 291 */ 249 public function get_version() { 292 public function get_version() 293 { 250 294 return $this->version; 251 295 } 252 296 253 297 254 public static function view( $name, array $args = array() ) { 255 $args = apply_filters( 'moderation_api_view_arguments', $args, $name ); 256 257 foreach ( $args AS $key => $val ) { 298 public static function view($name, array $args = array()) 299 { 300 $args = apply_filters('moderation_api_view_arguments', $args, $name); 301 302 foreach ($args as $key => $val) { 258 303 $$key = $val; 259 304 } 260 305 261 306 // load_plugin_textdomain( 'modapi' ); 262 307 263 $file = MODERATION_API_PLUGIN_DIR . 'admin/views/'. $name . '.php'; 264 265 include( $file ); 266 } 267 268 public static function get_api_key(){ 269 return get_option( 'moderation_api_key' ); 270 } 271 272 273 public static function display_status() { 274 275 } 276 277 278 public static function get_webhook_url() { 279 return home_url( '/moderation-api-webhook' ); 308 $file = MODERATION_API_PLUGIN_DIR . 'admin/views/' . $name . '.php'; 309 310 include($file); 311 } 312 313 public static function get_api_key() 314 { 315 if (is_multisite()) { 316 return Moderation_Api_Network_Admin::get_effective_api_key(); 317 } 318 return get_option('moderation_api_key'); 319 } 320 321 /** 322 * Check if moderation is disabled for the current site 323 */ 324 public static function is_moderation_disabled() 325 { 326 $site_disabled = get_option('modapi_site_disabled', '0') === '1'; 327 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 328 329 // Moderation is disabled if the site has it disabled AND network isn't enforced 330 return $site_disabled && !$network_enforced; 331 } 332 333 334 public static function display_status() 335 { 336 337 } 338 339 340 public static function get_webhook_url() 341 { 342 return home_url('/moderation-api-webhook'); 280 343 } 281 344 … … 283 346 * Display admin notices on the Comments page. 284 347 */ 285 public function add_admin_notices() { 348 public function add_admin_notices() 349 { 286 350 // Get the current screen 287 351 $screen = get_current_screen(); 288 352 289 353 // Check if we are on the Comments admin page 290 if ( 'edit-comments' === $screen->id) {354 if ('edit-comments' === $screen->id) { 291 355 $api_key = Moderation_Api::get_api_key(); 292 356 293 357 if (!$api_key) { 294 Moderation_Api::view('notice', array(295 'message' => __( 'Activate your Moderation API account to protect your site.', 'moderation-api'),296 'type'=> 'info', // Types: 'success', 'error', 'warning', 'info'297 ) );358 Moderation_Api::view('notice', array( 359 'message' => __('Activate your Moderation API account to protect your site.', 'moderation-api'), 360 'type' => 'info', // Types: 'success', 'error', 'warning', 'info' 361 )); 298 362 } 299 363 } -
moderation-api-automated-content-moderation/tags/1.0.3/moderation-api.php
r3177384 r3326772 17 17 * Plugin URI: https://moderationapi.com/integrations/wordpress-content-moderation 18 18 * Description: Use Moderation API to automatically moderate comments on your WordPress site. Detects a large range of contet such as bullying, discrimination, sentiment, NSFW, PII, and much more. 19 * Version: 1.0. 219 * Version: 1.0.3 20 20 * Author: Moderation API 21 21 * Author URI: https://moderationapi.com/ … … 36 36 * Rename this for your plugin and update it as you release new versions. 37 37 */ 38 define( 'MODERATION_API_VERSION', '1.0. 2' );38 define( 'MODERATION_API_VERSION', '1.0.3' ); 39 39 40 40 define( 'MODERATION_API_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); … … 62 62 63 63 /** 64 * The code that runs during plugin network activation for new sites. 65 */ 66 function moderation_api_network_activate() { 67 require_once plugin_dir_path( __FILE__ ) . 'includes/class-moderation-api-activator.php'; 68 Moderation_API_Activator::network_activate(); 69 } 70 71 if (is_multisite()) { 72 add_action('wpmu_new_blog', 'moderation_api_network_activate'); 73 } 74 75 /** 64 76 * The core plugin class that is used to define internationalization, 65 77 * admin-specific hooks, and public-facing site hooks. -
moderation-api-automated-content-moderation/tags/1.0.3/public/class-moderation-api-public.php
r3177384 r3326772 24 24 25 25 /** 26 * Static array to track processed comments to prevent duplicate API calls 27 * 28 * @since 1.0.0 29 * @access private 30 * @var array $processed_comments Array of comment hashes already processed 31 */ 32 private static $processed_comments = array(); 33 34 /** 26 35 * The ID of this plugin. 27 36 * … … 53 62 $this->version = $version; 54 63 55 // Add the pre-approval filter with priority 20 (after default filters)56 64 add_filter('pre_comment_approved', array($this, 'pre_comment_approved_filter'), 20, 2); 57 65 add_action('edit_comment', array($this, 'edit_comment_handler'), 10, 2); 58 66 } 59 67 … … 105 113 106 114 107 public function analyze_comment($comment ){115 public function analyze_comment($comment, $content_id = null){ 108 116 $api_key = Moderation_Api::get_api_key(); 109 117 … … 113 121 $user_email = sanitize_email($comment->comment_author_email); 114 122 $user_ip = sanitize_text_field($comment->comment_author_IP); 115 // $comment_id = absint($comment->comment_ID);116 123 $post_id = absint($comment->comment_post_ID); 117 124 $post_url = esc_url(get_permalink($post_id)); … … 124 131 $authorId = $user_ip; 125 132 } 133 134 // Predict comment ID for URL metadata 135 $last_comment = get_comments(array('number' => 1, 'orderby' => 'comment_ID', 'order' => 'DESC')); 136 $predicted_comment_id = $last_comment ? $last_comment[0]->comment_ID + 1 : 1; 126 137 127 138 $url = Moderation_Api::API_URL . "/api/v1/moderate/text"; … … 135 146 'contextId' => (string) $post_id, 136 147 'metadata' => array( 137 // 'comment_id' => $comment_id, 138 'url' => $post_url 148 'url' => $post_url . '#comment-' . $predicted_comment_id 139 149 ) 140 150 ); 151 152 // If content_id is provided (for edits), include it to maintain history 153 if ($content_id) { 154 $data['contentId'] = $content_id; 155 } 141 156 142 157 $response = wp_remote_post($url, array( … … 149 164 // Decode the JSON response 150 165 $responseData = json_decode($body, true); 166 167 // Keep contentId from API response as-is 168 151 169 // Return the response data 152 170 return $responseData; … … 179 197 $json = json_decode($raw_data, true); 180 198 $type = isset($json['type']) ? sanitize_text_field($json['type']) : ''; 181 $metadata = isset($json['metadata']) ? $json['metadata'] : array(); 182 $comment_id = isset($metadata['comment_id']) ? absint($metadata['comment_id']) : 0; 183 184 if($type == 'QUEUE_ITEM_ACTION' && $comment_id ){ 199 $item_id = isset($json['item']['id']) ? sanitize_text_field($json['item']['id']) : ''; 200 201 if($type == 'QUEUE_ITEM_ACTION' && $item_id) { 202 // Find comment by content ID (multisite compatible) 203 $result = $this->find_comment_by_content_id($item_id); 204 205 if (!$result) { 206 wp_send_json_error('Comment not found for item ID: ' . $item_id, 404); 207 exit(); 208 } 209 210 $comment_id = $result['comment_id']; 211 $site_id = $result['site_id']; 212 185 213 // Sanitize and validate the action 186 214 $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : ''; … … 191 219 } 192 220 193 $comment = get_comment($comment_id); 194 195 if(!$comment){ 196 wp_send_json_error('Comment not found', 404); 197 exit(); 221 // Switch to the correct site if multisite 222 if (is_multisite() && $site_id) { 223 switch_to_blog($site_id); 198 224 } 199 225 … … 213 239 } 214 240 241 // Restore original blog if we switched 242 if (is_multisite() && $site_id) { 243 restore_current_blog(); 244 } 245 215 246 wp_send_json_success($action); 216 247 exit(); … … 233 264 public function pre_comment_approved_filter($approved, $commentdata) { 234 265 try { 266 // Check if site moderation is disabled 267 if (Moderation_Api::is_moderation_disabled()) { 268 // Moderation is disabled for this site, skip analysis 269 return $approved; 270 } 271 235 272 // Convert commentdata array to object for consistency 236 273 $comment = (object) $commentdata; 237 238 // Analyze the comment 239 $analysis = $this->analyze_comment($comment); 240 241 if ($analysis === false) { 274 275 // Create unique hash for this comment to prevent duplicate API calls 276 $comment_hash = md5($comment->comment_content . $comment->comment_author_IP . $comment->comment_post_ID); 277 278 // Generate cache key for analysis results 279 $cache_key = 'modapi_analysis_' . md5($comment->comment_content . $comment->comment_author_IP); 280 281 // Check if we've already processed this comment 282 if (in_array($comment_hash, self::$processed_comments)) { 283 error_log("MOD_API Comment already processed - using cached result"); 284 $analysis = wp_cache_get($cache_key); 285 } else { 286 // Mark this comment as processed 287 self::$processed_comments[] = $comment_hash; 288 error_log("MOD_API Processing comment: " . $comment->comment_content); 289 290 $analysis = $this->analyze_comment($comment); 291 292 if ($analysis === false) { 242 293 // On API failure, let other filters handle it 243 294 return $approved; 244 } 245 246 // Store analysis results in temporary location 247 wp_cache_set('modapi_analysis_' . $comment->comment_author_IP, $analysis, '', 60); 248 249 250 if ($analysis['flagged']) { 251 $action = get_option('modapi_flagged_action', '0'); 295 } 252 296 253 switch ($action) { 254 case '1': 255 // Let comment through 256 return '1'; 257 258 case '2': 259 // Hold for moderation 260 return '0'; 261 262 case '3': 263 // Mark as spam 264 return 'spam'; 265 266 case '4': 267 // Send to trash 268 return 'trash'; 269 270 default: 271 // Default to moderation 272 return $approved; 273 } 274 } 297 wp_cache_set($cache_key, $analysis, '', 60); 298 } 299 300 // Use analysis result (either cached or fresh) to determine approval 301 if ($analysis) { 302 return $this->get_approval_status($analysis, $approved); 303 } 304 305 // Fallback if no analysis available 306 return $approved; 275 307 276 308 … … 287 319 288 320 public function moderation_api_comment_post( $comment_ID, $comment_approved ) { 321 // Get the comment object and reconstruct the cache key 322 $comment = get_comment($comment_ID); 323 $cache_key = 'modapi_analysis_' . md5($comment->comment_content . $comment->comment_author_IP); 324 289 325 // Get the cached analysis results 290 $analysis = wp_cache_get( 'modapi_analysis_' . get_comment($comment_ID)->comment_author_IP);326 $analysis = wp_cache_get($cache_key); 291 327 292 328 if ($analysis) { 293 329 // Store the analysis results as comment meta 294 330 add_comment_meta($comment_ID, 'modapi_flagged', $analysis['flagged']); 295 331 if (isset($analysis['contentId'])) { 332 add_comment_meta($comment_ID, 'modapi_content_id', $analysis['contentId']); 333 } 296 334 // Clear the cache 297 wp_cache_delete('modapi_analysis_' . get_comment($comment_ID)->comment_author_IP); 335 wp_cache_delete($cache_key); 336 } 337 } 338 339 340 /** 341 * Find comment by content ID across multisite network 342 * 343 * @param string $content_id The content ID to search for 344 * @return array|false Array with comment_id and site_id if found, false otherwise 345 */ 346 private function find_comment_by_content_id($content_id) { 347 if (is_multisite()) { 348 // Search across all sites in the network 349 $sites = get_sites(); 350 foreach ($sites as $site) { 351 switch_to_blog($site->blog_id); 352 353 $comments = get_comments(array( 354 'meta_key' => 'modapi_content_id', 355 'meta_value' => $content_id, 356 'number' => 1 357 )); 358 359 if (!empty($comments)) { 360 $result = array( 361 'comment_id' => $comments[0]->comment_ID, 362 'site_id' => $site->blog_id 363 ); 364 restore_current_blog(); 365 return $result; 366 } 367 368 restore_current_blog(); 369 } 370 return false; 371 } else { 372 // Single site - search current site only 373 $comments = get_comments(array( 374 'meta_key' => 'modapi_content_id', 375 'meta_value' => $content_id, 376 'number' => 1 377 )); 378 379 if (!empty($comments)) { 380 return array( 381 'comment_id' => $comments[0]->comment_ID, 382 'site_id' => null 383 ); 384 } 385 386 return false; 387 } 388 } 389 390 /** 391 * Handle comment edits by re-analyzing with existing content ID 392 * 393 * @param int $comment_ID The comment ID being edited 394 * @param array $data The comment data being updated 395 */ 396 public function edit_comment_handler($comment_ID, $data) { 397 // Get the existing comment 398 $comment = get_comment($comment_ID); 399 if (!$comment) { 400 return; 401 } 402 403 // Get the existing content ID to maintain history 404 $existing_content_id = get_comment_meta($comment_ID, 'modapi_content_id', true); 405 if (!$existing_content_id) { 406 // No existing content ID, skip analysis 407 return; 408 } 409 410 // Create a comment object with the new data for analysis 411 $updated_comment = (object) array_merge((array) $comment, $data); 412 413 // Analyze the updated comment with the existing content ID 414 $analysis = $this->analyze_comment($updated_comment, $existing_content_id); 415 416 if ($analysis !== false) { 417 // Update the comment meta with new analysis results 418 update_comment_meta($comment_ID, 'modapi_flagged', $analysis['flagged']); 419 420 // Update content ID if it changed (though it shouldn't for edits) 421 if (isset($analysis['contentId'])) { 422 update_comment_meta($comment_ID, 'modapi_content_id', $analysis['contentId']); 423 } 424 425 // Apply moderation action based on new analysis 426 $this->apply_moderation_action($comment_ID, $analysis); 427 } 428 } 429 430 /** 431 * Apply moderation action to a comment based on analysis results 432 * 433 * @param int $comment_ID The comment ID 434 * @param array $analysis The analysis results 435 */ 436 private function apply_moderation_action($comment_ID, $analysis) { 437 if (!$analysis['flagged']) { 438 // If not flagged, approve the comment 439 wp_set_comment_status($comment_ID, 'approve'); 440 return; 441 } 442 443 $action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action', '2'); 444 445 switch ($action) { 446 case '1': 447 // Let comment through (approve) 448 wp_set_comment_status($comment_ID, 'approve'); 449 break; 450 451 case '2': 452 // Hold for moderation 453 wp_set_comment_status($comment_ID, 'hold'); 454 break; 455 456 case '3': 457 // Mark as spam 458 wp_spam_comment($comment_ID); 459 break; 460 461 case '4': 462 // Send to trash 463 wp_trash_comment($comment_ID); 464 break; 465 466 default: 467 // Default to hold for moderation 468 wp_set_comment_status($comment_ID, 'hold'); 469 break; 470 } 471 } 472 473 /** 474 * Get the approval status based on analysis results 475 * 476 * @param array $analysis Analysis results from moderation API 477 * @param mixed $default_approved Default approval status 478 * @return mixed Approval status 479 */ 480 private function get_approval_status($analysis, $default_approved) { 481 if (!$analysis['flagged']) { 482 return $default_approved; 483 } 484 485 $action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action', '2'); 486 487 switch ($action) { 488 case '1': 489 // Let comment through 490 return '1'; 491 492 case '2': 493 // Hold for moderation 494 return '0'; 495 496 case '3': 497 // Mark as spam 498 return 'spam'; 499 500 case '4': 501 // Send to trash 502 return 'trash'; 503 504 default: 505 // Default to original approval status 506 return $default_approved; 298 507 } 299 508 } -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/admin/class-moderation-api-admin.php
r3161458 r3326772 21 21 * @author Moderation API <[email protected]> 22 22 */ 23 class Moderation_Api_Admin { 23 class Moderation_Api_Admin 24 { 24 25 25 26 const NONCE = 'modapi-update-key'; … … 51 52 * @param string $version The version of this plugin. 52 53 */ 53 public function __construct( $plugin_name, $version ) { 54 public function __construct($plugin_name, $version) 55 { 54 56 55 57 $this->plugin_name = $plugin_name; … … 57 59 58 60 } 59 60 61 62 61 63 /** 62 64 * Register the stylesheets for the admin area. … … 64 66 * @since 1.0.0 65 67 */ 66 public function enqueue_styles() { 68 public function enqueue_styles() 69 { 67 70 68 71 /** … … 78 81 */ 79 82 80 wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/moderation-api-admin.css', array(), $this->version, 'all');83 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/moderation-api-admin.css', array(), $this->version, 'all'); 81 84 82 85 } … … 87 90 * @since 1.0.0 88 91 */ 89 public function enqueue_scripts() { 92 public function enqueue_scripts() 93 { 90 94 91 95 /** … … 101 105 */ 102 106 103 wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/moderation-api-admin.js', array( 'jquery' ), $this->version, false ); 104 } 105 106 function check_nonce(){ 107 wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'js/moderation-api-admin.js', array('jquery'), $this->version, false); 108 } 109 110 function check_nonce() 111 { 107 112 // Verify nonce for security 108 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) , Moderation_Api_Admin::NONCE)) { 109 wp_nonce_ays("error"); 110 } 111 } 112 113 public function init() { 113 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), Moderation_Api_Admin::NONCE)) { 114 wp_nonce_ays("error"); 115 } 116 } 117 118 public function init() 119 { 114 120 // check page is moderation-api 115 121 if (!isset($_GET['page']) || $_GET['page'] !== 'moderation-api') { … … 133 139 } 134 140 135 private function handle_post_request(){ 141 private function handle_post_request() 142 { 136 143 $this->check_nonce(); 137 144 $action = $this->get_action(); … … 139 146 } 140 147 141 private function handle_get_request(){ 148 private function handle_get_request() 149 { 142 150 $action = $this->get_action(); 143 151 $this->handle_action($action); 144 152 } 145 153 146 private function get_action(){ 154 private function get_action() 155 { 147 156 // get action from post or get 148 157 $action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : null; … … 150 159 $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : null; 151 160 } 152 if (!$action && isset($_GET['token'])){161 if (!$action && isset($_GET['token'])) { 153 162 $action = 'enter-key'; 154 163 } … … 157 166 158 167 159 private function handle_action($action){ 168 private function handle_action($action) 169 { 160 170 if ($action === 'enter-key') { 161 171 $api_key = sanitize_text_field($_POST['key'] ?? $_GET['token']); … … 165 175 $this->disconnect_key(); 166 176 } 167 } 168 169 170 private function save_key($api_key){ 177 if ($action === 'toggle-site-moderation') { 178 $this->toggle_site_moderation(); 179 } 180 if ($action === 'update-flagged-action') { 181 $this->save_flagged_action(); 182 } 183 if ($action === 'use-network-api-key') { 184 $this->use_network_api_key(); 185 } 186 if ($action === 'use-network-flagged-action') { 187 $this->use_network_flagged_action(); 188 } 189 } 190 191 192 private function save_key($api_key) 193 { 171 194 if (!$api_key) { 172 195 return; … … 176 199 177 200 // invalid key show notice 178 add_action('admin_notices', function () {201 add_action('admin_notices', function () { 179 202 ?> 180 203 <div class="notice notice-error is-dismissible"> … … 186 209 } 187 210 188 foreach (array('modapi_flagged_action') as $option) {189 // Sanitize the input190 $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3';191 192 // Escape the output before saving it to the database193 $escaped_value = esc_attr($input_value);194 195 // Update the option in the database196 update_option($option, $escaped_value);197 }198 199 211 $existingKey = Moderation_Api::get_api_key(); 200 201 update_option('moderation_api_key', $api_key); 202 203 if(!$existingKey){ 204 $this->sync_actions(); 205 } 206 207 } 208 209 210 private function disconnect_key() { 212 213 update_option('moderation_api_key', $api_key); 214 215 if (!$existingKey) { 216 $this->sync_actions(); 217 } 218 219 } 220 221 private function save_flagged_action() 222 { 223 foreach (array('modapi_flagged_action') as $option) { 224 // Sanitize the input 225 $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3'; 226 227 // Escape the output before saving it to the database 228 $escaped_value = esc_attr($input_value); 229 230 // Update the option in the database 231 update_option($option, $escaped_value); 232 } 233 234 // Show success notice 235 add_action('admin_notices', function () { 236 ?> 237 <div class="notice notice-success is-dismissible"> 238 <p><?php esc_html_e('Flagged comments setting saved successfully.', 'modapi'); ?></p> 239 </div> 240 <?php 241 }); 242 } 243 244 private function use_network_api_key() 245 { 246 // Remove the site-specific API key to fall back to network key 247 update_option('moderation_api_key', ''); 248 249 // Show success notice 250 add_action('admin_notices', function () { 251 ?> 252 <div class="notice notice-success is-dismissible"> 253 <p><?php esc_html_e('Switched to network API key successfully.', 'modapi'); ?></p> 254 </div> 255 <?php 256 }); 257 } 258 259 private function use_network_flagged_action() 260 { 261 // Remove the site-specific flagged action to fall back to network setting 262 update_option('modapi_flagged_action', ''); 263 264 // Show success notice 265 add_action('admin_notices', function () { 266 ?> 267 <div class="notice notice-success is-dismissible"> 268 <p><?php esc_html_e('Switched to network flagged comment setting successfully.', 'modapi'); ?></p> 269 </div> 270 <?php 271 }); 272 } 273 274 275 private function disconnect_key() 276 { 211 277 // remove the api key 212 278 update_option('moderation_api_key', ''); 213 279 } 214 280 215 function get_account($key) { 281 private function toggle_site_moderation() 282 { 283 // Check if network settings are enforced 284 if (is_multisite() && get_site_option('modapi_enforce_network', '0') === '1') { 285 // Don't allow toggling when network is enforced 286 add_action('admin_notices', function () { 287 ?> 288 <div class="notice notice-error is-dismissible"> 289 <p><?php esc_html_e('Cannot disable moderation: network settings are enforced.', 'moderation-api'); ?></p> 290 </div> 291 <?php 292 }); 293 return; 294 } 295 296 $current_state = get_option('modapi_site_disabled', '0'); 297 $new_state = ($current_state === '1') ? '0' : '1'; 298 299 update_option('modapi_site_disabled', $new_state); 300 301 // Show success message 302 $message = ($new_state === '1') 303 ? __('Site moderation has been disabled.', 'moderation-api') 304 : __('Site moderation has been enabled.', 'moderation-api'); 305 306 add_action('admin_notices', function () use ($message) { 307 ?> 308 <div class="notice notice-success is-dismissible"> 309 <p><?php echo esc_html($message); ?></p> 310 </div> 311 <?php 312 }); 313 } 314 315 function get_account($key) 316 { 216 317 // include key as header bearer 217 318 $headers = array( … … 220 321 ); 221 322 $url = Moderation_Api::API_URL . "/api/v1/account"; 222 323 223 324 224 325 $response = wp_remote_get($url, array( … … 237 338 238 339 239 function validate_api_key($key) { 340 function validate_api_key($key) 341 { 240 342 241 343 if (!$key) { … … 243 345 } 244 346 try { 245 347 246 348 $account = $this->get_account($key); 247 349 if ($account && $account->id) { … … 258 360 259 361 260 public function add_admin_menu() { 362 public function add_admin_menu() 363 { 261 364 add_options_page('Moderation API', 'Moderation API', 'manage_options', 'moderation-api', array($this, 'display_page')); 262 365 } 263 366 264 public function display_page() { 367 public function display_page() 368 { 265 369 $api_key = Moderation_Api::get_api_key(); 266 370 267 371 if ($api_key) { 268 $account = $this->get_account($api_key); 269 if (!$account) { 270 $this->disconnect_key(); 271 return; 272 } 273 274 Moderation_Api::view('config', array('modapi_user' => $account)); 372 $account = $this->get_account($api_key); 373 if (!$account) { 374 $this->disconnect_key(); 275 375 return; 376 } 377 378 Moderation_Api::view('config', array('modapi_user' => $account)); 379 return; 276 380 } 277 381 … … 284 388 285 389 286 public static function get_page_url($page = 'config') { 390 public static function get_page_url($page = 'config') 391 { 287 392 288 393 $args = array('page' => 'moderation-api'); … … 298 403 return add_query_arg($args, menu_page_url('moderation-api', false)); 299 404 } 300 405 301 406 302 407 // Add a custom column to the comments list table 303 function custom_comments_column($columns) { 408 function custom_comments_column($columns) 409 { 304 410 // Add a new column 305 411 $columns['custom_flagged'] = 'Flagged'; … … 308 414 309 415 // Display the flagged value in the custom column 310 function show_flagged_custom_field($column_name, $comment_id) { 416 function show_flagged_custom_field($column_name, $comment_id) 417 { 311 418 if ($column_name === 'custom_flagged') { 312 $flagged = get_comment_meta($comment_id, 'modapi_flagged', true); 313 if($flagged){ 314 echo '<span style="color: red;">' . __('Flagged', 'moderation-api') . '</span>'; 315 }else{ 316 echo '<span style="color: green;">' . __('Not Flagged', 'moderation-api') . '</span>'; 317 } 318 } 319 } 320 321 322 function add_modapi_error_column($columns) { 323 $columns['modapi_error'] = __('Moderation API error', 'moderation-api'); 324 return $columns; 325 } 326 327 328 function populate_modapi_error_column($column, $comment_ID) { 329 if ('modapi_error' === $column) { 330 $modapi_error = get_comment_meta($comment_ID, 'modapi_error', true); 331 if ($modapi_error) { 332 echo esc_html($modapi_error); 333 } else { 334 echo __('', 'moderation-api'); 335 } 419 $flagged = get_comment_meta($comment_id, 'modapi_flagged', true); 420 if ($flagged) { 421 echo '<span style="color: red;">' . __('Flagged', 'moderation-api') . '</span>'; 422 } else { 423 echo '<span style="color: green;">' . __('Not Flagged', 'moderation-api') . '</span>'; 336 424 } 337 } 338 339 340 private function sync_actions() { 425 } 426 } 427 428 429 function add_modapi_error_column($columns) 430 { 431 $columns['modapi_error'] = __('Moderation API error', 'moderation-api'); 432 return $columns; 433 } 434 435 436 function populate_modapi_error_column($column, $comment_ID) 437 { 438 if ('modapi_error' === $column) { 439 $modapi_error = get_comment_meta($comment_ID, 'modapi_error', true); 440 if ($modapi_error) { 441 echo esc_html($modapi_error); 442 } else { 443 echo __('', 'moderation-api'); 444 } 445 } 446 } 447 448 449 private function sync_actions() 450 { 341 451 342 452 $apiKey = Moderation_Api::get_api_key(); … … 355 465 ) 356 466 ) 357 ),467 ), 358 468 array( 359 469 'name' => 'Show on WP', -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/admin/css/moderation-api-admin.css
r3177384 r3326772 466 466 } 467 467 468 .modapi-card .modapi-card-actions {469 margin-top: 1rem;470 }471 472 468 .jetpack_page_modapi-key-config .update-nag, 473 469 .settings_page_moderation-api .update-nag { … … 537 533 .modapi-box-header { 538 534 max-width: 700px; 539 margin: 0 auto 40px auto;535 margin: 0 auto 0px auto; 540 536 line-height: 1.5; 541 537 } … … 627 623 } 628 624 629 .modapi-button.secondary {625 #modapi-plugin-container .modapi-button.secondary { 630 626 background: #f5f5f5; 631 627 border-color: #c8d7e1; 632 628 color: #2e4453; 633 629 } 634 .modapi-button.secondary:hover {630 #modapi-plugin-container .modapi-button.secondary:hover { 635 631 background: #e6ecf1; 636 632 border-color: #a8bece; … … 886 882 } 887 883 884 .modapi-settings__row-input .description { 885 margin-top: 0.5em; 886 } 887 888 888 .modapi-settings__row-title { 889 889 font-weight: 500; … … 891 891 margin: 0; 892 892 margin-bottom: 1em; 893 align-items: center; 893 894 } 894 895 … … 899 900 .modapi-card-actions { 900 901 padding: 1em; 902 display: flex; 903 justify-content: space-between; 904 align-items: center; 905 } 906 907 .modapi-card-actions-extra { 908 position: absolute; 909 left: 1em; 910 bottom: 1em; 911 display: flex; 912 align-items: center; 901 913 } 902 914 903 915 .modapi-settings__row label { 904 916 padding-bottom: 1em; 917 } 918 .modapi-settings__row-title label { 919 padding-bottom: 0 !important; 905 920 } 906 921 -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/admin/views/config.php
r3143763 r3326772 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 2 if (!defined('ABSPATH')) 3 exit; // Exit if accessed directly 3 4 4 5 //phpcs:disable VariableAnalysis … … 9 10 <div class="modapi-masthead"> 10 11 <div class="modapi-masthead__inside-container"> 11 <?php Moderation_Api::view( 'logo'); ?>12 <?php Moderation_Api::view('logo'); ?> 12 13 </div> 13 14 </div> 14 15 <div class="modapi-lower"> 15 <?php if ( Moderation_Api::get_api_key()) { ?>16 <?php if (Moderation_Api::get_api_key()) { ?> 16 17 <?php Moderation_Api::display_status(); ?> 17 18 <?php } ?> 18 <?php if ( ! empty( $notices )) { ?>19 <?php foreach ( $notices as $notice) { ?>20 <?php Moderation_Api::view( 'notice', $notice); ?>19 <?php if (!empty($notices)) { ?> 20 <?php foreach ($notices as $notice) { ?> 21 <?php Moderation_Api::view('notice', $notice); ?> 21 22 <?php } ?> 22 23 <?php } ?> 23 24 24 25 26 <div class="modapi-card"> 27 <div class="modapi-section-header"> 28 <h2 class="modapi-section-header__label"> 29 <span><?php esc_html_e( 'Account' , 'modapi'); ?></span> 30 </h2> 25 26 27 <div class="modapi-card"> 28 <div class="modapi-section-header"> 29 <h2 class="modapi-section-header__label"> 30 <span><?php esc_html_e('Account', 'modapi'); ?></span> 31 </h2> 32 </div> 33 34 <div class="inside"> 35 <table class="modapi-account"> 36 <tbody> 37 <tr> 38 <th scope="row"><?php esc_html_e('Project', 'modapi'); ?></th> 39 <td> 40 <?php echo esc_html($modapi_user->current_project->name); ?> 41 </td> 42 </tr> 43 44 <tr> 45 <th scope="row"><?php esc_html_e('Subscription type', 'modapi'); ?></th> 46 <td> 47 <?php echo esc_html($modapi_user->paid_plan_name); ?> 48 </td> 49 </tr> 50 51 52 <tr> 53 <th scope="row"><?php esc_html_e('Quota', 'modapi'); ?></th> 54 <td> 55 <?php echo number_format(esc_html($modapi_user->text_api_quota)); ?> 56 </td> 57 </tr> 58 59 <tr> 60 <th scope="row"><?php esc_html_e('Remaining quota', 'modapi'); ?></th> 61 <td> 62 <?php echo number_format(esc_html($modapi_user->remaining_quota)); ?> 63 </td> 64 </tr> 65 66 67 </tbody> 68 </table> 69 <div class="modapi-settings__row"></div> 70 <div class="modapi-card-actions"> 71 <div id="delete-action"> 72 <a class="submitdelete deletion" 73 href="<?php echo esc_url(Moderation_Api_Admin::get_page_url('delete_key')); ?>"><?php esc_html_e('Disconnect this account', 'modapi'); ?></a> 31 74 </div> 32 75 33 <div class="inside"> 34 <table class="modapi-account"> 35 <tbody> 36 <tr> 37 <th scope="row"><?php esc_html_e( 'Project', 'modapi' ); ?></th> 38 <td> 39 <?php echo esc_html( $modapi_user->current_project->name ); ?> 40 </td> 41 </tr> 42 43 <tr> 44 <th scope="row"><?php esc_html_e( 'Subscription type', 'modapi' ); ?></th> 45 <td> 46 <?php echo esc_html( $modapi_user->paid_plan_name ); ?> 47 </td> 48 </tr> 49 50 51 <tr> 52 <th scope="row"><?php esc_html_e( 'Quota', 'modapi' ); ?></th> 53 <td> 54 <?php echo number_format(esc_html( $modapi_user->text_api_quota )); ?> 55 </td> 56 </tr> 57 58 <tr> 59 <th scope="row"><?php esc_html_e( 'Remaining quota', 'modapi' ); ?></th> 60 <td> 61 <?php echo number_format(esc_html( $modapi_user->remaining_quota )); ?> 62 </td> 63 </tr> 64 65 66 </tbody> 67 </table> 68 <div class="modapi-settings__row"></div> 69 <div class="modapi-card-actions"> 70 <div id="delete-action"> 71 <a class="submitdelete deletion" href="<?php echo esc_url( Moderation_Api_Admin::get_page_url( 'delete_key' ) ); ?>"><?php esc_html_e( 'Disconnect this account', 'modapi' ); ?></a> 72 </div> 73 74 <div id="publishing-action"> 75 <a href="https://moderationapi.com/app/upgrade" target="_blank"> 76 <button type="button" class="modapi-button " > 77 <?php esc_attr_e( 'Upgrade plan', 'modapi' ); ?> 78 </button> 79 </a> 80 </div> 81 <div class="clear"></div> 82 </div> 76 <div id="publishing-action"> 77 <a href="https://moderationapi.com/app/upgrade" target="_blank"> 78 <button type="button" class="modapi-button "> 79 <?php esc_attr_e('Upgrade plan', 'modapi'); ?> 80 </button> 81 </a> 83 82 </div> 84 83 </div> 85 84 </div> 85 </div> 86 87 <?php if (is_multisite()): ?> 88 <?php 89 $network_api_key = get_site_option('moderation_api_network_key'); 90 $enforce_network = get_site_option('modapi_enforce_network', '0'); 91 $can_override = Moderation_Api_Network_Admin::can_override_network_settings(); 92 $using_network_key = !get_option('moderation_api_key') && $network_api_key; 93 ?> 86 94 <div class="modapi-card"> 87 95 <div class="modapi-section-header"> 88 96 <h2 class="modapi-section-header__label"> 89 <span><?php esc_html_e( 'Settings', 'modapi'); ?></span>97 <span><?php esc_html_e('Multisite Settings', 'modapi'); ?></span> 90 98 </h2> 91 99 </div> 92 100 93 101 <div class="inside"> 94 <form action="<?php echo esc_url( Moderation_Api_Admin::get_page_url() ); ?>" autocomplete="off" method="POST" id="modapi-settings-form"> 95 102 <div class="modapi-settings"> 103 104 105 <div class="modapi-settings__row"> 106 <h3 class="modapi-settings__row-title"> 107 <label 108 class="modapi-settings__row-label"><?php esc_html_e('Site Moderation', 'modapi'); ?></label> 109 </h3> 110 <div style="margin-bottom: 10px;"> 111 <?php 112 $site_disabled = get_option('modapi_site_disabled', '0'); 113 $network_enforced = $enforce_network === '1'; 114 ?> 115 <?php if ($network_enforced): ?> 116 <span 117 style="background: #646970; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 118 <?php esc_html_e('NETWORK CONTROLLED', 'modapi'); ?> 119 </span> 120 <p style="color: #646970; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 121 <?php esc_html_e('Moderation cannot be disabled when network settings are enforced.', 'modapi'); ?> 122 </p> 123 <?php else: ?> 124 <?php if ($site_disabled === '1'): ?> 125 <span 126 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 127 <?php esc_html_e('MODERATION DISABLED', 'modapi'); ?> 128 </span> 129 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 130 <?php esc_html_e('Comments will not be analyzed by Moderation API on this site.', 'modapi'); ?> 131 </p> 132 <?php else: ?> 133 134 <p style="font-size: 13px; margin: 5px 0 0 0; "> 135 <?php esc_html_e('Disable moderation for this site only.', 'modapi'); ?> 136 </p> 137 <?php endif; ?> 138 <?php endif; ?> 139 </div> 140 <div class="modapi-settings__row-input"> 141 <?php if (!$network_enforced): ?> 142 <form method="post" style="display: flex; flex-direction: column; align-items: flex-start;"> 143 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 144 <input type="hidden" name="action" value="toggle-site-moderation"> 145 <?php if ($site_disabled === '1'): ?> 146 <input type="submit" name="submit" 147 value="<?php esc_attr_e('Enable Moderation', 'modapi'); ?>" 148 class=" modapi-button secondary"> 149 <?php else: ?> 150 <input type="submit" name="submit" 151 value="<?php esc_attr_e('Disable Moderation For This Site', 'modapi'); ?>" 152 class=" modapi-button secondary"> 153 <?php endif; ?> 154 </form> 155 <?php endif; ?> 156 </div> 157 </div> 158 159 <?php if (current_user_can('manage_network_options')): ?> 160 <div class="modapi-settings__row"> 161 <h3 class="modapi-settings__row-title"> 162 <label 163 class="modapi-settings__row-label"><?php esc_html_e('Network Admin', 'modapi'); ?></label> 164 </h3> 165 <div class="modapi-settings__row-input"> 166 <a href="<?php echo esc_url(network_admin_url('settings.php?page=moderation-api-network')); ?>" 167 class=" modapi-button secondary"> 168 <?php esc_html_e('Manage Network Settings', 'modapi'); ?> 169 </a> 170 </div> 171 </div> 172 <?php endif; ?> 173 </div> 174 </div> 175 </div> 176 <?php endif; ?> 177 178 <?php 179 // Ensure $site_disabled is always defined 180 if (!isset($site_disabled)) { 181 $site_disabled = get_option('modapi_site_disabled', '0'); 182 } 183 ?> 184 185 <?php if ($site_disabled !== '1'): ?> 186 187 <div class="modapi-card"> 188 <div class="modapi-section-header"> 189 <h2 class="modapi-section-header__label"> 190 <span><?php esc_html_e('API Configuration', 'modapi'); ?></span> 191 </h2> 192 </div> 193 194 <div class="inside"> 195 <form action="<?php echo esc_url(Moderation_Api_Admin::get_page_url()); ?>" autocomplete="off" 196 method="POST" id="modapi-settings-form"> 197 96 198 <div class="modapi-settings"> 97 <div class="modapi-settings__row"> 199 <div class="modapi-settings__row"> 200 <?php 201 $site_api_key = get_option('moderation_api_key'); 202 $network_api_key = get_site_option('moderation_api_network_key'); 203 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 204 $using_site_key = !empty($site_api_key) && !$network_enforced; 205 $using_network_key = !$using_site_key && !empty($network_api_key); 206 ?> 207 <h3 class="modapi-settings__row-title"> 208 <label class="modapi-settings__row-label" 209 for="key"><?php esc_html_e('API key', 'modapi'); ?></label> 210 </h3> 211 <?php if (is_multisite() && ($using_site_key || $using_network_key || $network_enforced)): ?> 212 <div style="margin-bottom: 10px;"> 213 <?php if ($network_enforced): ?> 214 <span 215 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 216 <?php esc_html_e('NETWORK ENFORCED', 'modapi'); ?> 217 </span> 218 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 219 <?php esc_html_e('This API key is enforced by network settings and cannot be changed at the site level.', 'modapi'); ?> 220 </p> 221 <?php elseif ($using_site_key): ?> 222 <span 223 style="background: #dba617; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 224 <?php esc_html_e('SITE OVERRIDE', 'modapi'); ?> 225 </span> 226 <p style="color: #dba617; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 227 <?php esc_html_e('This site is using a custom API key that overrides the network default.', 'modapi'); ?> 228 </p> 229 <?php elseif ($using_network_key): ?> 230 <span 231 style="background: #00a32a; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 232 <?php esc_html_e('NETWORK DEFAULT', 'modapi'); ?> 233 </span> 234 <p style="color: #00a32a; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 235 <?php esc_html_e('This site is using the network-wide API key.', 'modapi'); ?> 236 </p> 237 <?php endif; ?> 238 </div> 239 <?php endif; ?> 240 <div class="modapi-settings__row-input"> 241 <?php 242 $site_api_key = get_option('moderation_api_key'); 243 $network_api_key = get_site_option('moderation_api_network_key'); 244 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 245 $using_site_key = !empty($site_api_key) && !$network_enforced; 246 $using_network_key = !$using_site_key && !empty($network_api_key); 247 248 if ($network_enforced) { 249 $api_key_value = $network_api_key; 250 } else if ($using_site_key) { 251 $api_key_value = $site_api_key; 252 } else { 253 $api_key_value = $network_api_key ?: ''; 254 } 255 ?> 256 257 <span class="api-key"> 258 <input id="key" name="key" type="text" style="width: 100%;" 259 value="<?php echo esc_attr($api_key_value); ?>" <?php echo $network_enforced ? 'readonly disabled' : ''; ?>> 260 </span> 261 262 263 </div> 264 </div> 265 266 </div> 267 268 <?php if (!$network_enforced): ?> 269 <div class="modapi-card-actions"> 270 271 272 273 <div style="flex: 1;"></div> 274 275 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 276 277 <div id="publishing-action"> 278 <input type="hidden" name="action" value="enter-key"> 279 <input type="submit" name="submit" id="submit" class="modapi-button modapi-could-be-primary" 280 value="<?php esc_attr_e('Save API Key', 'modapi'); ?>"> 281 </div> 282 </div> 283 <?php endif; ?> 284 </form> 285 286 287 <?php if ($using_site_key && !empty($network_api_key)): ?> 288 <div class="modapi-card-actions-extra"> 289 <form method="post" style="margin-top: 10px;"> 290 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 291 <input type="hidden" name="action" value="use-network-api-key"> 292 <input type="submit" name="submit" value="<?php esc_attr_e('Use Network API Key', 'modapi'); ?>" 293 class="modapi-button secondary" style="font-size: 12px; padding: 4px 8px;"> 294 </form> 295 </div> 296 <?php endif; ?> 297 298 299 </div> 300 </div> 301 302 <div class="modapi-card"> 303 <div class="modapi-section-header"> 304 <h2 class="modapi-section-header__label"> 305 <span><?php esc_html_e('Comment Moderation', 'modapi'); ?></span> 306 </h2> 307 </div> 308 309 <div class="inside"> 310 <div class="modapi-settings"> 311 <div class="modapi-settings__row"> 312 <h3 class="modapi-settings__row-title"> 313 <label 314 class="modapi-settings__row-label"><?php esc_html_e('Content filter', 'modapi'); ?></label> 315 </h3> 316 <div class="modapi-settings__row-input"> 317 <a href="<?php echo esc_url(Moderation_Api::API_URL . '/app/projects/' . esc_attr($modapi_user->current_project->id)); ?>" 318 target="_blank"> 319 <button type="button" class="modapi-button secondary"> 320 <?php esc_attr_e('Edit content filter', 'modapi'); ?> 321 </button> 322 </a> 323 </div> 324 </div> 325 </div> 326 327 <form action="<?php echo esc_url(Moderation_Api_Admin::get_page_url()); ?>" autocomplete="off" 328 method="POST" id="modapi-flagged-comments-form"> 329 <div class="modapi-settings"> 330 <div class="modapi-settings__row is-radio"> 331 <?php 332 $site_flagged_action = get_option('modapi_flagged_action'); 333 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 334 $effective_action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action'); 335 $radio_disabled = $network_enforced ? 'disabled' : ''; 336 337 // More precise logic for determining setting source 338 $has_site_flagged = ($site_flagged_action !== false && $site_flagged_action !== ''); 339 $has_network_flagged = ($network_flagged_action !== false && $network_flagged_action !== ''); 340 $using_site_flagged = $has_site_flagged && !$network_enforced; 341 $using_network_flagged = !$has_site_flagged && $has_network_flagged && !$network_enforced; 342 343 // Debug info (remove in production) 344 if (WP_DEBUG) { 345 echo "<!-- DEBUG: site_flagged_action: " . var_export($site_flagged_action, true) . " -->"; 346 echo "<!-- DEBUG: network_flagged_action: " . var_export($network_flagged_action, true) . " -->"; 347 echo "<!-- DEBUG: has_site_flagged: " . var_export($has_site_flagged, true) . " -->"; 348 echo "<!-- DEBUG: has_network_flagged: " . var_export($has_network_flagged, true) . " -->"; 349 echo "<!-- DEBUG: using_site_flagged: " . var_export($using_site_flagged, true) . " -->"; 350 echo "<!-- DEBUG: network_enforced: " . var_export($network_enforced, true) . " -->"; 351 } 352 ?> 353 <div class="modapi-settings__row-text"> 98 354 <h3 class="modapi-settings__row-title"> 99 < label class="modapi-settings__row-label" for="key"><?php esc_html_e( 'API key', 'modapi' ); ?></label>355 <?php esc_html_e('Flagged comments', 'modapi'); ?> 100 356 </h3> 101 <div class="modapi-settings__row-input"> 102 <span class="api-key"><input id="key" name="key" type="text" style="width: 100%;" value="<?php echo esc_attr( get_option('moderation_api_key') ); ?>" ></span> 103 </div> 104 </div> 105 106 107 <div class="modapi-settings__row"> 108 <h3 class="modapi-settings__row-title"> 109 <label class="modapi-settings__row-label" for="key"><?php esc_html_e( 'Content filter', 'modapi' ); ?></label> 110 </h3> 111 <div class="modapi-settings__row-input"> 112 <a href="<?php echo esc_url(Moderation_Api::API_URL . '/app/projects/' . esc_attr($modapi_user->current_project->id)); ?>" target="_blank"> 113 <button type="button" class="modapi-button secondary" > 114 <?php esc_attr_e( 'Edit content filter', 'modapi' ); ?> 115 </button> 116 </a> 117 </div> 118 119 </div> 120 121 122 123 <div class="modapi-settings__row is-radio"> 124 <div class="modapi-settings__row-text"> 125 <h3 class="modapi-settings__row-title"><?php esc_html_e( 'Flagged comments', 'modapi' ); ?></h3> 357 <?php if (is_multisite() && ($using_site_flagged || $using_network_flagged || $network_enforced)): ?> 358 <div style="margin-bottom: 15px;"> 359 <?php if ($network_enforced): ?> 360 <span 361 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 362 <?php esc_html_e('NETWORK ENFORCED', 'modapi'); ?> 363 </span> 364 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 365 <?php esc_html_e('These settings are enforced by network configuration and cannot be changed at the site level.', 'modapi'); ?> 366 </p> 367 <?php elseif ($using_site_flagged): ?> 368 <span 369 style="background: #dba617; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 370 <?php esc_html_e('SITE OVERRIDE', 'modapi'); ?> 371 </span> 372 <p style="color: #dba617; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 373 <?php esc_html_e('This site is using custom flagged comment behavior that overrides the network default.', 'modapi'); ?> 374 </p> 375 <?php elseif ($using_network_flagged): ?> 376 <span 377 style="background: #00a32a; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 378 <?php esc_html_e('NETWORK DEFAULT', 'modapi'); ?> 379 </span> 380 <p style="color: #00a32a; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 381 <?php esc_html_e('This site is using the network-wide flagged comment behavior.', 'modapi'); ?> 382 </p> 383 <?php endif; ?> 384 </div> 385 <?php endif; ?> 126 386 </div> 127 387 <div class="modapi-settings__row-input"> 128 <fieldset> 129 388 <?php 389 $site_flagged_action = get_option('modapi_flagged_action'); 390 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 391 $effective_action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action'); 392 $radio_disabled = $network_enforced ? 'disabled' : ''; 393 394 // More precise logic for determining setting source 395 $has_site_flagged = ($site_flagged_action !== false && $site_flagged_action !== ''); 396 $has_network_flagged = ($network_flagged_action !== false && $network_flagged_action !== ''); 397 $using_site_flagged = $has_site_flagged && !$network_enforced; 398 $using_network_flagged = !$has_site_flagged && $has_network_flagged && !$network_enforced; 399 ?> 400 <fieldset <?php echo $network_enforced ? 'disabled' : ''; ?>> 401 130 402 <div> 131 403 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_3"> 132 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_3" value="3" <?php checked( '3', get_option( 'modapi_flagged_action' ) ); ?> /> 404 <input type="radio" name="modapi_flagged_action" 405 id="modapi_flagged_action_3" value="3" <?php checked('3', $effective_action); ?> <?php echo $radio_disabled; ?> /> 133 406 <span class="modapi-settings__row-label-text"> 134 <?php esc_html_e( 'Move flagged comments to spam.', 'modapi'); ?>407 <?php esc_html_e('Move flagged comments to spam.', 'modapi'); ?> 135 408 </span> 136 409 </label> … … 138 411 <div> 139 412 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_2"> 140 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_2" value="2" <?php checked( '2', get_option( 'modapi_flagged_action' ) ); ?> /> 413 <input type="radio" name="modapi_flagged_action" 414 id="modapi_flagged_action_2" value="2" <?php checked('2', $effective_action); ?> <?php echo $radio_disabled; ?> /> 141 415 <span class="modapi-settings__row-label-text"> 142 <?php esc_html_e( 'Move flagged comments to pending for review.', 'modapi'); ?>416 <?php esc_html_e('Move flagged comments to pending for review.', 'modapi'); ?> 143 417 </span> 144 418 </label> … … 147 421 <div> 148 422 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_4"> 149 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_4" value="4" <?php checked( '4', get_option( 'modapi_flagged_action' ) ); ?> /> 423 <input type="radio" name="modapi_flagged_action" 424 id="modapi_flagged_action_4" value="4" <?php checked('4', $effective_action); ?> <?php echo $radio_disabled; ?> /> 150 425 <span class="modapi-settings__row-label-text"> 151 <?php esc_html_e( 'Move flagged comments to trash.', 'modapi'); ?>426 <?php esc_html_e('Move flagged comments to trash.', 'modapi'); ?> 152 427 </span> 153 428 </label> 154 429 </div> 155 430 156 431 <div> 157 432 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_1"> 158 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_1" value="1" <?php checked( '1', get_option( 'modapi_flagged_action' ) ); ?> /> 433 <input type="radio" name="modapi_flagged_action" 434 id="modapi_flagged_action_1" value="1" <?php checked('1', $effective_action); ?> <?php echo $radio_disabled; ?> /> 159 435 <span class="modapi-settings__row-label-text"> 160 <?php esc_html_e( 'Move flagged comments to approved.', 'modapi'); ?>436 <?php esc_html_e('Move flagged comments to approved.', 'modapi'); ?> 161 437 </span> 162 438 </label> … … 164 440 <div> 165 441 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_0"> 166 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_0" value="0" <?php checked( '0', get_option( 'modapi_flagged_action' ) ); ?> /> 442 <input type="radio" name="modapi_flagged_action" 443 id="modapi_flagged_action_0" value="0" <?php checked('0', $effective_action); ?> <?php echo $radio_disabled; ?> /> 167 444 <span class="modapi-settings__row-label-text"> 168 <?php esc_html_e( 'Do not do anything.', 'modapi'); ?>445 <?php esc_html_e('Do not do anything.', 'modapi'); ?> 169 446 </span> 170 447 </label> … … 174 451 175 452 <div class="modapi-settings__row-note"> 176 <strong><?php esc_html_e( 'Note:', 'modapi' ); ?></strong> 177 We recommend to use the <a href="https://moderationapi.com/app/moderation/queue" target="_blank">content queue</a> in the Moderation API dashboard to review flagged comments. 453 <strong><?php esc_html_e('Note:', 'modapi'); ?></strong> 454 We recommend to use the <a href="https://moderationapi.com/app/moderation/queue" 455 target="_blank">content queue</a> in the Moderation API dashboard to review 456 flagged comments. 178 457 </div> 179 458 180 459 </div> 181 460 </div> 182 183 184 461 </div> 185 186 <div class="modapi-card-actions"> 187 188 189 <?php wp_nonce_field( Moderation_Api_Admin::NONCE ); ?> 190 191 <div id="publishing-action"> 192 <input type="hidden" name="action" value="enter-key"> 193 <input type="submit" name="submit" id="submit" class="modapi-button modapi-could-be-primary" value="<?php esc_attr_e( 'Save changes', 'modapi' ); ?>"> 194 </div> 195 <div class="clear"></div> 462 463 <?php if (!$network_enforced): ?> 464 <div class="modapi-card-actions"> 465 466 467 <div style="flex: 1;"></div> 468 469 470 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 471 472 <div id="publishing-action"> 473 <input type="hidden" name="action" value="update-flagged-action"> 474 <input type="submit" name="submit" id="submit-flagged" 475 class="modapi-button modapi-could-be-primary" 476 value="<?php esc_attr_e('Save', 'modapi'); ?>"> 477 </div> 478 </div> 479 <?php endif; ?> 480 </form> 481 482 483 <?php if (is_multisite() && $using_site_flagged && $has_network_flagged && !$network_enforced): ?> 484 <div class="modapi-card-actions-extra"> 485 <form method="post" class=""> 486 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 487 <input type="hidden" name="action" value="use-network-flagged-action"> 488 <input type="submit" name="submit" value="<?php esc_attr_e('Use Network Setting', 'modapi'); ?>" 489 class="modapi-button secondary" style="font-size: 12px; padding: 4px 8px;"> 490 </form> 491 492 493 </form> 494 <span style="margin-left: 10px; color: #666; font-size: 12px;"> 495 <?php esc_html_e('Remove site override and use network default', 'modapi'); ?> 496 </span> 196 497 </div> 197 </form> 498 <?php endif; ?> 499 500 198 501 </div> 199 502 </div> 200 503 201 504 <?php endif; // End if site is not disabled ?> 505 202 506 </div> 203 507 </div> -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/development.md
r3177384 r3326772 13 13 npx @wordpress/env start 14 14 ``` 15 16 ## Admin credentials 17 18 admin 19 password -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/includes/class-moderation-api-activator.php
r3143763 r3326772 31 31 */ 32 32 public static function activate() { 33 // Initialize default flagged action setting if not already set 34 if (get_option('modapi_flagged_action') === false) { 35 add_option('modapi_flagged_action', '2'); 36 } 37 } 33 38 39 /** 40 * Network activation - Initialize default settings for new sites 41 * 42 * @since 1.0.3 43 */ 44 public static function network_activate() { 45 // Don't create local flagged action setting for new sites 46 // This allows them to inherit the network setting through get_effective_flagged_action() 47 48 // Only create the option if there's no network setting at all 49 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 50 if ($network_flagged_action === false || $network_flagged_action === '') { 51 // No network setting exists, create a local default 52 if (get_option('modapi_flagged_action') === false) { 53 add_option('modapi_flagged_action', '2'); 54 } 55 } 56 // Otherwise, leave no local option so the site inherits network settings 34 57 } 35 58 -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/includes/class-moderation-api.php
r3177384 r3326772 28 28 * @author Moderation API <[email protected]> 29 29 */ 30 class Moderation_Api { 30 class Moderation_Api 31 { 31 32 32 33 /** … … 70 71 * @since 1.0.0 71 72 */ 72 public function __construct() { 73 if ( defined( 'MODERATION_API_VERSION' ) ) { 73 public function __construct() 74 { 75 if (defined('MODERATION_API_VERSION')) { 74 76 $this->version = MODERATION_API_VERSION; 75 77 } else { … … 83 85 $this->define_public_hooks(); 84 86 87 if (is_multisite()) { 88 $this->define_network_admin_hooks(); 89 } 90 85 91 } 86 92 … … 101 107 * @access private 102 108 */ 103 private function load_dependencies() { 109 private function load_dependencies() 110 { 104 111 105 112 /** … … 107 114 * core plugin. 108 115 */ 109 require_once plugin_dir_path( dirname( __FILE__ )) . 'includes/class-moderation-api-loader.php';116 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-moderation-api-loader.php'; 110 117 111 118 /** … … 113 120 * of the plugin. 114 121 */ 115 require_once plugin_dir_path( dirname( __FILE__ )) . 'includes/class-moderation-api-i18n.php';122 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-moderation-api-i18n.php'; 116 123 117 124 /** 118 125 * The class responsible for defining all actions that occur in the admin area. 119 126 */ 120 require_once plugin_dir_path( dirname( __FILE__ )) . 'admin/class-moderation-api-admin.php';127 require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-moderation-api-admin.php'; 121 128 122 129 /** … … 124 131 * side of the site. 125 132 */ 126 require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-moderation-api-public.php'; 133 require_once plugin_dir_path(dirname(__FILE__)) . 'public/class-moderation-api-public.php'; 134 135 /** 136 * The class responsible for defining network admin functionality in multisite. 137 */ 138 if (is_multisite()) { 139 require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-moderation-api-network-admin.php'; 140 } 127 141 128 142 $this->loader = new Moderation_Api_Loader(); … … 139 153 * @access private 140 154 */ 141 private function set_locale() { 155 private function set_locale() 156 { 142 157 143 158 $plugin_i18n = new Moderation_Api_i18n(); 144 159 145 $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');160 $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain'); 146 161 147 162 } … … 154 169 * @access private 155 170 */ 156 private function define_admin_hooks() { 157 158 $plugin_admin = new Moderation_Api_Admin( $this->get_plugin_name(), $this->get_version() ); 159 160 $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); 161 $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); 171 private function define_admin_hooks() 172 { 173 174 $plugin_admin = new Moderation_Api_Admin($this->get_plugin_name(), $this->get_version()); 175 176 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles'); 177 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts'); 162 178 163 179 // add admin sidepanel 164 $this->loader->add_action( 'admin_menu', $plugin_admin, 'add_admin_menu');180 $this->loader->add_action('admin_menu', $plugin_admin, 'add_admin_menu'); 165 181 166 182 // hook for admin settings page 167 $this->loader->add_action( 'admin_init', $plugin_admin, 'init');168 169 170 $this->loader->add_filter( 'manage_edit-comments_columns', $plugin_admin, 'custom_comments_column');171 $this->loader->add_action( 'manage_comments_custom_column', $plugin_admin, 'show_flagged_custom_field', 10, 2);183 $this->loader->add_action('admin_init', $plugin_admin, 'init'); 184 185 186 $this->loader->add_filter('manage_edit-comments_columns', $plugin_admin, 'custom_comments_column'); 187 $this->loader->add_action('manage_comments_custom_column', $plugin_admin, 'show_flagged_custom_field', 10, 2); 172 188 173 189 … … 177 193 // Add a custom column to the comments list table 178 194 $this->loader->add_filter('manage_edit-comments_columns', $plugin_admin, 'add_modapi_error_column'); 179 180 $this->loader->add_action( 'admin_notices', $this, 'add_admin_notices');195 196 $this->loader->add_action('admin_notices', $this, 'add_admin_notices'); 181 197 182 198 } … … 191 207 * @access private 192 208 */ 193 private function define_public_hooks() { 194 195 $plugin_public = new Moderation_Api_Public( $this->get_plugin_name(), $this->get_version() ); 196 197 $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); 198 $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); 209 private function define_public_hooks() 210 { 211 212 $plugin_public = new Moderation_Api_Public($this->get_plugin_name(), $this->get_version()); 213 214 $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_styles'); 215 $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_scripts'); 199 216 200 217 $this->loader->add_action('comment_post', $plugin_public, 'moderation_api_comment_post', 10, 2); … … 207 224 } 208 225 209 210 211 226 /** 227 * Register all of the hooks related to the network admin functionality 228 * of the plugin. 229 * 230 * @since 1.0.3 231 * @access private 232 */ 233 private function define_network_admin_hooks() 234 { 235 236 $plugin_network_admin = new Moderation_Api_Network_Admin($this->get_plugin_name(), $this->get_version()); 237 238 $this->loader->add_action('network_admin_enqueue_scripts', $plugin_network_admin, 'enqueue_styles'); 239 $this->loader->add_action('network_admin_enqueue_scripts', $plugin_network_admin, 'enqueue_scripts'); 240 241 // Add network admin menu 242 $this->loader->add_action('network_admin_menu', $plugin_network_admin, 'add_network_admin_menu'); 243 244 // Hook for network admin settings page processing 245 $this->loader->add_action('admin_init', $plugin_network_admin, 'init'); 246 $this->loader->add_action('network_admin_init', $plugin_network_admin, 'init'); 247 } 248 249 250 251 212 252 213 253 /** … … 216 256 * @since 1.0.0 217 257 */ 218 public function run() { 258 public function run() 259 { 219 260 $this->loader->run(); 220 261 } … … 227 268 * @return string The name of the plugin. 228 269 */ 229 public function get_plugin_name() { 270 public function get_plugin_name() 271 { 230 272 return $this->plugin_name; 231 273 } … … 237 279 * @return Moderation_Api_Loader Orchestrates the hooks of the plugin. 238 280 */ 239 public function get_loader() { 281 public function get_loader() 282 { 240 283 return $this->loader; 241 284 } … … 247 290 * @return string The version number of the plugin. 248 291 */ 249 public function get_version() { 292 public function get_version() 293 { 250 294 return $this->version; 251 295 } 252 296 253 297 254 public static function view( $name, array $args = array() ) { 255 $args = apply_filters( 'moderation_api_view_arguments', $args, $name ); 256 257 foreach ( $args AS $key => $val ) { 298 public static function view($name, array $args = array()) 299 { 300 $args = apply_filters('moderation_api_view_arguments', $args, $name); 301 302 foreach ($args as $key => $val) { 258 303 $$key = $val; 259 304 } 260 305 261 306 // load_plugin_textdomain( 'modapi' ); 262 307 263 $file = MODERATION_API_PLUGIN_DIR . 'admin/views/'. $name . '.php'; 264 265 include( $file ); 266 } 267 268 public static function get_api_key(){ 269 return get_option( 'moderation_api_key' ); 270 } 271 272 273 public static function display_status() { 274 275 } 276 277 278 public static function get_webhook_url() { 279 return home_url( '/moderation-api-webhook' ); 308 $file = MODERATION_API_PLUGIN_DIR . 'admin/views/' . $name . '.php'; 309 310 include($file); 311 } 312 313 public static function get_api_key() 314 { 315 if (is_multisite()) { 316 return Moderation_Api_Network_Admin::get_effective_api_key(); 317 } 318 return get_option('moderation_api_key'); 319 } 320 321 /** 322 * Check if moderation is disabled for the current site 323 */ 324 public static function is_moderation_disabled() 325 { 326 $site_disabled = get_option('modapi_site_disabled', '0') === '1'; 327 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 328 329 // Moderation is disabled if the site has it disabled AND network isn't enforced 330 return $site_disabled && !$network_enforced; 331 } 332 333 334 public static function display_status() 335 { 336 337 } 338 339 340 public static function get_webhook_url() 341 { 342 return home_url('/moderation-api-webhook'); 280 343 } 281 344 … … 283 346 * Display admin notices on the Comments page. 284 347 */ 285 public function add_admin_notices() { 348 public function add_admin_notices() 349 { 286 350 // Get the current screen 287 351 $screen = get_current_screen(); 288 352 289 353 // Check if we are on the Comments admin page 290 if ( 'edit-comments' === $screen->id) {354 if ('edit-comments' === $screen->id) { 291 355 $api_key = Moderation_Api::get_api_key(); 292 356 293 357 if (!$api_key) { 294 Moderation_Api::view('notice', array(295 'message' => __( 'Activate your Moderation API account to protect your site.', 'moderation-api'),296 'type'=> 'info', // Types: 'success', 'error', 'warning', 'info'297 ) );358 Moderation_Api::view('notice', array( 359 'message' => __('Activate your Moderation API account to protect your site.', 'moderation-api'), 360 'type' => 'info', // Types: 'success', 'error', 'warning', 'info' 361 )); 298 362 } 299 363 } -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/moderation-api.php
r3177384 r3326772 17 17 * Plugin URI: https://moderationapi.com/integrations/wordpress-content-moderation 18 18 * Description: Use Moderation API to automatically moderate comments on your WordPress site. Detects a large range of contet such as bullying, discrimination, sentiment, NSFW, PII, and much more. 19 * Version: 1.0. 219 * Version: 1.0.3 20 20 * Author: Moderation API 21 21 * Author URI: https://moderationapi.com/ … … 36 36 * Rename this for your plugin and update it as you release new versions. 37 37 */ 38 define( 'MODERATION_API_VERSION', '1.0. 2' );38 define( 'MODERATION_API_VERSION', '1.0.3' ); 39 39 40 40 define( 'MODERATION_API_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); … … 62 62 63 63 /** 64 * The code that runs during plugin network activation for new sites. 65 */ 66 function moderation_api_network_activate() { 67 require_once plugin_dir_path( __FILE__ ) . 'includes/class-moderation-api-activator.php'; 68 Moderation_API_Activator::network_activate(); 69 } 70 71 if (is_multisite()) { 72 add_action('wpmu_new_blog', 'moderation_api_network_activate'); 73 } 74 75 /** 64 76 * The core plugin class that is used to define internationalization, 65 77 * admin-specific hooks, and public-facing site hooks. -
moderation-api-automated-content-moderation/tags/1.0.3/trunk/public/class-moderation-api-public.php
r3177384 r3326772 24 24 25 25 /** 26 * Static array to track processed comments to prevent duplicate API calls 27 * 28 * @since 1.0.0 29 * @access private 30 * @var array $processed_comments Array of comment hashes already processed 31 */ 32 private static $processed_comments = array(); 33 34 /** 26 35 * The ID of this plugin. 27 36 * … … 53 62 $this->version = $version; 54 63 55 // Add the pre-approval filter with priority 20 (after default filters)56 64 add_filter('pre_comment_approved', array($this, 'pre_comment_approved_filter'), 20, 2); 57 65 add_action('edit_comment', array($this, 'edit_comment_handler'), 10, 2); 58 66 } 59 67 … … 105 113 106 114 107 public function analyze_comment($comment ){115 public function analyze_comment($comment, $content_id = null){ 108 116 $api_key = Moderation_Api::get_api_key(); 109 117 … … 113 121 $user_email = sanitize_email($comment->comment_author_email); 114 122 $user_ip = sanitize_text_field($comment->comment_author_IP); 115 // $comment_id = absint($comment->comment_ID);116 123 $post_id = absint($comment->comment_post_ID); 117 124 $post_url = esc_url(get_permalink($post_id)); … … 124 131 $authorId = $user_ip; 125 132 } 133 134 // Predict comment ID for URL metadata 135 $last_comment = get_comments(array('number' => 1, 'orderby' => 'comment_ID', 'order' => 'DESC')); 136 $predicted_comment_id = $last_comment ? $last_comment[0]->comment_ID + 1 : 1; 126 137 127 138 $url = Moderation_Api::API_URL . "/api/v1/moderate/text"; … … 135 146 'contextId' => (string) $post_id, 136 147 'metadata' => array( 137 // 'comment_id' => $comment_id, 138 'url' => $post_url 148 'url' => $post_url . '#comment-' . $predicted_comment_id 139 149 ) 140 150 ); 151 152 // If content_id is provided (for edits), include it to maintain history 153 if ($content_id) { 154 $data['contentId'] = $content_id; 155 } 141 156 142 157 $response = wp_remote_post($url, array( … … 149 164 // Decode the JSON response 150 165 $responseData = json_decode($body, true); 166 167 // Keep contentId from API response as-is 168 151 169 // Return the response data 152 170 return $responseData; … … 179 197 $json = json_decode($raw_data, true); 180 198 $type = isset($json['type']) ? sanitize_text_field($json['type']) : ''; 181 $metadata = isset($json['metadata']) ? $json['metadata'] : array(); 182 $comment_id = isset($metadata['comment_id']) ? absint($metadata['comment_id']) : 0; 183 184 if($type == 'QUEUE_ITEM_ACTION' && $comment_id ){ 199 $item_id = isset($json['item']['id']) ? sanitize_text_field($json['item']['id']) : ''; 200 201 if($type == 'QUEUE_ITEM_ACTION' && $item_id) { 202 // Find comment by content ID (multisite compatible) 203 $result = $this->find_comment_by_content_id($item_id); 204 205 if (!$result) { 206 wp_send_json_error('Comment not found for item ID: ' . $item_id, 404); 207 exit(); 208 } 209 210 $comment_id = $result['comment_id']; 211 $site_id = $result['site_id']; 212 185 213 // Sanitize and validate the action 186 214 $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : ''; … … 191 219 } 192 220 193 $comment = get_comment($comment_id); 194 195 if(!$comment){ 196 wp_send_json_error('Comment not found', 404); 197 exit(); 221 // Switch to the correct site if multisite 222 if (is_multisite() && $site_id) { 223 switch_to_blog($site_id); 198 224 } 199 225 … … 213 239 } 214 240 241 // Restore original blog if we switched 242 if (is_multisite() && $site_id) { 243 restore_current_blog(); 244 } 245 215 246 wp_send_json_success($action); 216 247 exit(); … … 233 264 public function pre_comment_approved_filter($approved, $commentdata) { 234 265 try { 266 // Check if site moderation is disabled 267 if (Moderation_Api::is_moderation_disabled()) { 268 // Moderation is disabled for this site, skip analysis 269 return $approved; 270 } 271 235 272 // Convert commentdata array to object for consistency 236 273 $comment = (object) $commentdata; 237 238 // Analyze the comment 239 $analysis = $this->analyze_comment($comment); 240 241 if ($analysis === false) { 274 275 // Create unique hash for this comment to prevent duplicate API calls 276 $comment_hash = md5($comment->comment_content . $comment->comment_author_IP . $comment->comment_post_ID); 277 278 // Generate cache key for analysis results 279 $cache_key = 'modapi_analysis_' . md5($comment->comment_content . $comment->comment_author_IP); 280 281 // Check if we've already processed this comment 282 if (in_array($comment_hash, self::$processed_comments)) { 283 error_log("MOD_API Comment already processed - using cached result"); 284 $analysis = wp_cache_get($cache_key); 285 } else { 286 // Mark this comment as processed 287 self::$processed_comments[] = $comment_hash; 288 error_log("MOD_API Processing comment: " . $comment->comment_content); 289 290 $analysis = $this->analyze_comment($comment); 291 292 if ($analysis === false) { 242 293 // On API failure, let other filters handle it 243 294 return $approved; 244 } 245 246 // Store analysis results in temporary location 247 wp_cache_set('modapi_analysis_' . $comment->comment_author_IP, $analysis, '', 60); 248 249 250 if ($analysis['flagged']) { 251 $action = get_option('modapi_flagged_action', '0'); 295 } 252 296 253 switch ($action) { 254 case '1': 255 // Let comment through 256 return '1'; 257 258 case '2': 259 // Hold for moderation 260 return '0'; 261 262 case '3': 263 // Mark as spam 264 return 'spam'; 265 266 case '4': 267 // Send to trash 268 return 'trash'; 269 270 default: 271 // Default to moderation 272 return $approved; 273 } 274 } 297 wp_cache_set($cache_key, $analysis, '', 60); 298 } 299 300 // Use analysis result (either cached or fresh) to determine approval 301 if ($analysis) { 302 return $this->get_approval_status($analysis, $approved); 303 } 304 305 // Fallback if no analysis available 306 return $approved; 275 307 276 308 … … 287 319 288 320 public function moderation_api_comment_post( $comment_ID, $comment_approved ) { 321 // Get the comment object and reconstruct the cache key 322 $comment = get_comment($comment_ID); 323 $cache_key = 'modapi_analysis_' . md5($comment->comment_content . $comment->comment_author_IP); 324 289 325 // Get the cached analysis results 290 $analysis = wp_cache_get( 'modapi_analysis_' . get_comment($comment_ID)->comment_author_IP);326 $analysis = wp_cache_get($cache_key); 291 327 292 328 if ($analysis) { 293 329 // Store the analysis results as comment meta 294 330 add_comment_meta($comment_ID, 'modapi_flagged', $analysis['flagged']); 295 331 if (isset($analysis['contentId'])) { 332 add_comment_meta($comment_ID, 'modapi_content_id', $analysis['contentId']); 333 } 296 334 // Clear the cache 297 wp_cache_delete('modapi_analysis_' . get_comment($comment_ID)->comment_author_IP); 335 wp_cache_delete($cache_key); 336 } 337 } 338 339 340 /** 341 * Find comment by content ID across multisite network 342 * 343 * @param string $content_id The content ID to search for 344 * @return array|false Array with comment_id and site_id if found, false otherwise 345 */ 346 private function find_comment_by_content_id($content_id) { 347 if (is_multisite()) { 348 // Search across all sites in the network 349 $sites = get_sites(); 350 foreach ($sites as $site) { 351 switch_to_blog($site->blog_id); 352 353 $comments = get_comments(array( 354 'meta_key' => 'modapi_content_id', 355 'meta_value' => $content_id, 356 'number' => 1 357 )); 358 359 if (!empty($comments)) { 360 $result = array( 361 'comment_id' => $comments[0]->comment_ID, 362 'site_id' => $site->blog_id 363 ); 364 restore_current_blog(); 365 return $result; 366 } 367 368 restore_current_blog(); 369 } 370 return false; 371 } else { 372 // Single site - search current site only 373 $comments = get_comments(array( 374 'meta_key' => 'modapi_content_id', 375 'meta_value' => $content_id, 376 'number' => 1 377 )); 378 379 if (!empty($comments)) { 380 return array( 381 'comment_id' => $comments[0]->comment_ID, 382 'site_id' => null 383 ); 384 } 385 386 return false; 387 } 388 } 389 390 /** 391 * Handle comment edits by re-analyzing with existing content ID 392 * 393 * @param int $comment_ID The comment ID being edited 394 * @param array $data The comment data being updated 395 */ 396 public function edit_comment_handler($comment_ID, $data) { 397 // Get the existing comment 398 $comment = get_comment($comment_ID); 399 if (!$comment) { 400 return; 401 } 402 403 // Get the existing content ID to maintain history 404 $existing_content_id = get_comment_meta($comment_ID, 'modapi_content_id', true); 405 if (!$existing_content_id) { 406 // No existing content ID, skip analysis 407 return; 408 } 409 410 // Create a comment object with the new data for analysis 411 $updated_comment = (object) array_merge((array) $comment, $data); 412 413 // Analyze the updated comment with the existing content ID 414 $analysis = $this->analyze_comment($updated_comment, $existing_content_id); 415 416 if ($analysis !== false) { 417 // Update the comment meta with new analysis results 418 update_comment_meta($comment_ID, 'modapi_flagged', $analysis['flagged']); 419 420 // Update content ID if it changed (though it shouldn't for edits) 421 if (isset($analysis['contentId'])) { 422 update_comment_meta($comment_ID, 'modapi_content_id', $analysis['contentId']); 423 } 424 425 // Apply moderation action based on new analysis 426 $this->apply_moderation_action($comment_ID, $analysis); 427 } 428 } 429 430 /** 431 * Apply moderation action to a comment based on analysis results 432 * 433 * @param int $comment_ID The comment ID 434 * @param array $analysis The analysis results 435 */ 436 private function apply_moderation_action($comment_ID, $analysis) { 437 if (!$analysis['flagged']) { 438 // If not flagged, approve the comment 439 wp_set_comment_status($comment_ID, 'approve'); 440 return; 441 } 442 443 $action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action', '2'); 444 445 switch ($action) { 446 case '1': 447 // Let comment through (approve) 448 wp_set_comment_status($comment_ID, 'approve'); 449 break; 450 451 case '2': 452 // Hold for moderation 453 wp_set_comment_status($comment_ID, 'hold'); 454 break; 455 456 case '3': 457 // Mark as spam 458 wp_spam_comment($comment_ID); 459 break; 460 461 case '4': 462 // Send to trash 463 wp_trash_comment($comment_ID); 464 break; 465 466 default: 467 // Default to hold for moderation 468 wp_set_comment_status($comment_ID, 'hold'); 469 break; 470 } 471 } 472 473 /** 474 * Get the approval status based on analysis results 475 * 476 * @param array $analysis Analysis results from moderation API 477 * @param mixed $default_approved Default approval status 478 * @return mixed Approval status 479 */ 480 private function get_approval_status($analysis, $default_approved) { 481 if (!$analysis['flagged']) { 482 return $default_approved; 483 } 484 485 $action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action', '2'); 486 487 switch ($action) { 488 case '1': 489 // Let comment through 490 return '1'; 491 492 case '2': 493 // Hold for moderation 494 return '0'; 495 496 case '3': 497 // Mark as spam 498 return 'spam'; 499 500 case '4': 501 // Send to trash 502 return 'trash'; 503 504 default: 505 // Default to original approval status 506 return $default_approved; 298 507 } 299 508 } -
moderation-api-automated-content-moderation/trunk/admin/class-moderation-api-admin.php
r3161458 r3326772 21 21 * @author Moderation API <[email protected]> 22 22 */ 23 class Moderation_Api_Admin { 23 class Moderation_Api_Admin 24 { 24 25 25 26 const NONCE = 'modapi-update-key'; … … 51 52 * @param string $version The version of this plugin. 52 53 */ 53 public function __construct( $plugin_name, $version ) { 54 public function __construct($plugin_name, $version) 55 { 54 56 55 57 $this->plugin_name = $plugin_name; … … 57 59 58 60 } 59 60 61 62 61 63 /** 62 64 * Register the stylesheets for the admin area. … … 64 66 * @since 1.0.0 65 67 */ 66 public function enqueue_styles() { 68 public function enqueue_styles() 69 { 67 70 68 71 /** … … 78 81 */ 79 82 80 wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/moderation-api-admin.css', array(), $this->version, 'all');83 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/moderation-api-admin.css', array(), $this->version, 'all'); 81 84 82 85 } … … 87 90 * @since 1.0.0 88 91 */ 89 public function enqueue_scripts() { 92 public function enqueue_scripts() 93 { 90 94 91 95 /** … … 101 105 */ 102 106 103 wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/moderation-api-admin.js', array( 'jquery' ), $this->version, false ); 104 } 105 106 function check_nonce(){ 107 wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'js/moderation-api-admin.js', array('jquery'), $this->version, false); 108 } 109 110 function check_nonce() 111 { 107 112 // Verify nonce for security 108 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) , Moderation_Api_Admin::NONCE)) { 109 wp_nonce_ays("error"); 110 } 111 } 112 113 public function init() { 113 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), Moderation_Api_Admin::NONCE)) { 114 wp_nonce_ays("error"); 115 } 116 } 117 118 public function init() 119 { 114 120 // check page is moderation-api 115 121 if (!isset($_GET['page']) || $_GET['page'] !== 'moderation-api') { … … 133 139 } 134 140 135 private function handle_post_request(){ 141 private function handle_post_request() 142 { 136 143 $this->check_nonce(); 137 144 $action = $this->get_action(); … … 139 146 } 140 147 141 private function handle_get_request(){ 148 private function handle_get_request() 149 { 142 150 $action = $this->get_action(); 143 151 $this->handle_action($action); 144 152 } 145 153 146 private function get_action(){ 154 private function get_action() 155 { 147 156 // get action from post or get 148 157 $action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : null; … … 150 159 $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : null; 151 160 } 152 if (!$action && isset($_GET['token'])){161 if (!$action && isset($_GET['token'])) { 153 162 $action = 'enter-key'; 154 163 } … … 157 166 158 167 159 private function handle_action($action){ 168 private function handle_action($action) 169 { 160 170 if ($action === 'enter-key') { 161 171 $api_key = sanitize_text_field($_POST['key'] ?? $_GET['token']); … … 165 175 $this->disconnect_key(); 166 176 } 167 } 168 169 170 private function save_key($api_key){ 177 if ($action === 'toggle-site-moderation') { 178 $this->toggle_site_moderation(); 179 } 180 if ($action === 'update-flagged-action') { 181 $this->save_flagged_action(); 182 } 183 if ($action === 'use-network-api-key') { 184 $this->use_network_api_key(); 185 } 186 if ($action === 'use-network-flagged-action') { 187 $this->use_network_flagged_action(); 188 } 189 } 190 191 192 private function save_key($api_key) 193 { 171 194 if (!$api_key) { 172 195 return; … … 176 199 177 200 // invalid key show notice 178 add_action('admin_notices', function () {201 add_action('admin_notices', function () { 179 202 ?> 180 203 <div class="notice notice-error is-dismissible"> … … 186 209 } 187 210 188 foreach (array('modapi_flagged_action') as $option) {189 // Sanitize the input190 $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3';191 192 // Escape the output before saving it to the database193 $escaped_value = esc_attr($input_value);194 195 // Update the option in the database196 update_option($option, $escaped_value);197 }198 199 211 $existingKey = Moderation_Api::get_api_key(); 200 201 update_option('moderation_api_key', $api_key); 202 203 if(!$existingKey){ 204 $this->sync_actions(); 205 } 206 207 } 208 209 210 private function disconnect_key() { 212 213 update_option('moderation_api_key', $api_key); 214 215 if (!$existingKey) { 216 $this->sync_actions(); 217 } 218 219 } 220 221 private function save_flagged_action() 222 { 223 foreach (array('modapi_flagged_action') as $option) { 224 // Sanitize the input 225 $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3'; 226 227 // Escape the output before saving it to the database 228 $escaped_value = esc_attr($input_value); 229 230 // Update the option in the database 231 update_option($option, $escaped_value); 232 } 233 234 // Show success notice 235 add_action('admin_notices', function () { 236 ?> 237 <div class="notice notice-success is-dismissible"> 238 <p><?php esc_html_e('Flagged comments setting saved successfully.', 'modapi'); ?></p> 239 </div> 240 <?php 241 }); 242 } 243 244 private function use_network_api_key() 245 { 246 // Remove the site-specific API key to fall back to network key 247 update_option('moderation_api_key', ''); 248 249 // Show success notice 250 add_action('admin_notices', function () { 251 ?> 252 <div class="notice notice-success is-dismissible"> 253 <p><?php esc_html_e('Switched to network API key successfully.', 'modapi'); ?></p> 254 </div> 255 <?php 256 }); 257 } 258 259 private function use_network_flagged_action() 260 { 261 // Remove the site-specific flagged action to fall back to network setting 262 update_option('modapi_flagged_action', ''); 263 264 // Show success notice 265 add_action('admin_notices', function () { 266 ?> 267 <div class="notice notice-success is-dismissible"> 268 <p><?php esc_html_e('Switched to network flagged comment setting successfully.', 'modapi'); ?></p> 269 </div> 270 <?php 271 }); 272 } 273 274 275 private function disconnect_key() 276 { 211 277 // remove the api key 212 278 update_option('moderation_api_key', ''); 213 279 } 214 280 215 function get_account($key) { 281 private function toggle_site_moderation() 282 { 283 // Check if network settings are enforced 284 if (is_multisite() && get_site_option('modapi_enforce_network', '0') === '1') { 285 // Don't allow toggling when network is enforced 286 add_action('admin_notices', function () { 287 ?> 288 <div class="notice notice-error is-dismissible"> 289 <p><?php esc_html_e('Cannot disable moderation: network settings are enforced.', 'moderation-api'); ?></p> 290 </div> 291 <?php 292 }); 293 return; 294 } 295 296 $current_state = get_option('modapi_site_disabled', '0'); 297 $new_state = ($current_state === '1') ? '0' : '1'; 298 299 update_option('modapi_site_disabled', $new_state); 300 301 // Show success message 302 $message = ($new_state === '1') 303 ? __('Site moderation has been disabled.', 'moderation-api') 304 : __('Site moderation has been enabled.', 'moderation-api'); 305 306 add_action('admin_notices', function () use ($message) { 307 ?> 308 <div class="notice notice-success is-dismissible"> 309 <p><?php echo esc_html($message); ?></p> 310 </div> 311 <?php 312 }); 313 } 314 315 function get_account($key) 316 { 216 317 // include key as header bearer 217 318 $headers = array( … … 220 321 ); 221 322 $url = Moderation_Api::API_URL . "/api/v1/account"; 222 323 223 324 224 325 $response = wp_remote_get($url, array( … … 237 338 238 339 239 function validate_api_key($key) { 340 function validate_api_key($key) 341 { 240 342 241 343 if (!$key) { … … 243 345 } 244 346 try { 245 347 246 348 $account = $this->get_account($key); 247 349 if ($account && $account->id) { … … 258 360 259 361 260 public function add_admin_menu() { 362 public function add_admin_menu() 363 { 261 364 add_options_page('Moderation API', 'Moderation API', 'manage_options', 'moderation-api', array($this, 'display_page')); 262 365 } 263 366 264 public function display_page() { 367 public function display_page() 368 { 265 369 $api_key = Moderation_Api::get_api_key(); 266 370 267 371 if ($api_key) { 268 $account = $this->get_account($api_key); 269 if (!$account) { 270 $this->disconnect_key(); 271 return; 272 } 273 274 Moderation_Api::view('config', array('modapi_user' => $account)); 372 $account = $this->get_account($api_key); 373 if (!$account) { 374 $this->disconnect_key(); 275 375 return; 376 } 377 378 Moderation_Api::view('config', array('modapi_user' => $account)); 379 return; 276 380 } 277 381 … … 284 388 285 389 286 public static function get_page_url($page = 'config') { 390 public static function get_page_url($page = 'config') 391 { 287 392 288 393 $args = array('page' => 'moderation-api'); … … 298 403 return add_query_arg($args, menu_page_url('moderation-api', false)); 299 404 } 300 405 301 406 302 407 // Add a custom column to the comments list table 303 function custom_comments_column($columns) { 408 function custom_comments_column($columns) 409 { 304 410 // Add a new column 305 411 $columns['custom_flagged'] = 'Flagged'; … … 308 414 309 415 // Display the flagged value in the custom column 310 function show_flagged_custom_field($column_name, $comment_id) { 416 function show_flagged_custom_field($column_name, $comment_id) 417 { 311 418 if ($column_name === 'custom_flagged') { 312 $flagged = get_comment_meta($comment_id, 'modapi_flagged', true); 313 if($flagged){ 314 echo '<span style="color: red;">' . __('Flagged', 'moderation-api') . '</span>'; 315 }else{ 316 echo '<span style="color: green;">' . __('Not Flagged', 'moderation-api') . '</span>'; 317 } 318 } 319 } 320 321 322 function add_modapi_error_column($columns) { 323 $columns['modapi_error'] = __('Moderation API error', 'moderation-api'); 324 return $columns; 325 } 326 327 328 function populate_modapi_error_column($column, $comment_ID) { 329 if ('modapi_error' === $column) { 330 $modapi_error = get_comment_meta($comment_ID, 'modapi_error', true); 331 if ($modapi_error) { 332 echo esc_html($modapi_error); 333 } else { 334 echo __('', 'moderation-api'); 335 } 419 $flagged = get_comment_meta($comment_id, 'modapi_flagged', true); 420 if ($flagged) { 421 echo '<span style="color: red;">' . __('Flagged', 'moderation-api') . '</span>'; 422 } else { 423 echo '<span style="color: green;">' . __('Not Flagged', 'moderation-api') . '</span>'; 336 424 } 337 } 338 339 340 private function sync_actions() { 425 } 426 } 427 428 429 function add_modapi_error_column($columns) 430 { 431 $columns['modapi_error'] = __('Moderation API error', 'moderation-api'); 432 return $columns; 433 } 434 435 436 function populate_modapi_error_column($column, $comment_ID) 437 { 438 if ('modapi_error' === $column) { 439 $modapi_error = get_comment_meta($comment_ID, 'modapi_error', true); 440 if ($modapi_error) { 441 echo esc_html($modapi_error); 442 } else { 443 echo __('', 'moderation-api'); 444 } 445 } 446 } 447 448 449 private function sync_actions() 450 { 341 451 342 452 $apiKey = Moderation_Api::get_api_key(); … … 355 465 ) 356 466 ) 357 ),467 ), 358 468 array( 359 469 'name' => 'Show on WP', -
moderation-api-automated-content-moderation/trunk/admin/css/moderation-api-admin.css
r3177384 r3326772 466 466 } 467 467 468 .modapi-card .modapi-card-actions {469 margin-top: 1rem;470 }471 472 468 .jetpack_page_modapi-key-config .update-nag, 473 469 .settings_page_moderation-api .update-nag { … … 537 533 .modapi-box-header { 538 534 max-width: 700px; 539 margin: 0 auto 40px auto;535 margin: 0 auto 0px auto; 540 536 line-height: 1.5; 541 537 } … … 627 623 } 628 624 629 .modapi-button.secondary {625 #modapi-plugin-container .modapi-button.secondary { 630 626 background: #f5f5f5; 631 627 border-color: #c8d7e1; 632 628 color: #2e4453; 633 629 } 634 .modapi-button.secondary:hover {630 #modapi-plugin-container .modapi-button.secondary:hover { 635 631 background: #e6ecf1; 636 632 border-color: #a8bece; … … 886 882 } 887 883 884 .modapi-settings__row-input .description { 885 margin-top: 0.5em; 886 } 887 888 888 .modapi-settings__row-title { 889 889 font-weight: 500; … … 891 891 margin: 0; 892 892 margin-bottom: 1em; 893 align-items: center; 893 894 } 894 895 … … 899 900 .modapi-card-actions { 900 901 padding: 1em; 902 display: flex; 903 justify-content: space-between; 904 align-items: center; 905 } 906 907 .modapi-card-actions-extra { 908 position: absolute; 909 left: 1em; 910 bottom: 1em; 911 display: flex; 912 align-items: center; 901 913 } 902 914 903 915 .modapi-settings__row label { 904 916 padding-bottom: 1em; 917 } 918 .modapi-settings__row-title label { 919 padding-bottom: 0 !important; 905 920 } 906 921 -
moderation-api-automated-content-moderation/trunk/admin/views/config.php
r3143763 r3326772 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 2 if (!defined('ABSPATH')) 3 exit; // Exit if accessed directly 3 4 4 5 //phpcs:disable VariableAnalysis … … 9 10 <div class="modapi-masthead"> 10 11 <div class="modapi-masthead__inside-container"> 11 <?php Moderation_Api::view( 'logo'); ?>12 <?php Moderation_Api::view('logo'); ?> 12 13 </div> 13 14 </div> 14 15 <div class="modapi-lower"> 15 <?php if ( Moderation_Api::get_api_key()) { ?>16 <?php if (Moderation_Api::get_api_key()) { ?> 16 17 <?php Moderation_Api::display_status(); ?> 17 18 <?php } ?> 18 <?php if ( ! empty( $notices )) { ?>19 <?php foreach ( $notices as $notice) { ?>20 <?php Moderation_Api::view( 'notice', $notice); ?>19 <?php if (!empty($notices)) { ?> 20 <?php foreach ($notices as $notice) { ?> 21 <?php Moderation_Api::view('notice', $notice); ?> 21 22 <?php } ?> 22 23 <?php } ?> 23 24 24 25 26 <div class="modapi-card"> 27 <div class="modapi-section-header"> 28 <h2 class="modapi-section-header__label"> 29 <span><?php esc_html_e( 'Account' , 'modapi'); ?></span> 30 </h2> 25 26 27 <div class="modapi-card"> 28 <div class="modapi-section-header"> 29 <h2 class="modapi-section-header__label"> 30 <span><?php esc_html_e('Account', 'modapi'); ?></span> 31 </h2> 32 </div> 33 34 <div class="inside"> 35 <table class="modapi-account"> 36 <tbody> 37 <tr> 38 <th scope="row"><?php esc_html_e('Project', 'modapi'); ?></th> 39 <td> 40 <?php echo esc_html($modapi_user->current_project->name); ?> 41 </td> 42 </tr> 43 44 <tr> 45 <th scope="row"><?php esc_html_e('Subscription type', 'modapi'); ?></th> 46 <td> 47 <?php echo esc_html($modapi_user->paid_plan_name); ?> 48 </td> 49 </tr> 50 51 52 <tr> 53 <th scope="row"><?php esc_html_e('Quota', 'modapi'); ?></th> 54 <td> 55 <?php echo number_format(esc_html($modapi_user->text_api_quota)); ?> 56 </td> 57 </tr> 58 59 <tr> 60 <th scope="row"><?php esc_html_e('Remaining quota', 'modapi'); ?></th> 61 <td> 62 <?php echo number_format(esc_html($modapi_user->remaining_quota)); ?> 63 </td> 64 </tr> 65 66 67 </tbody> 68 </table> 69 <div class="modapi-settings__row"></div> 70 <div class="modapi-card-actions"> 71 <div id="delete-action"> 72 <a class="submitdelete deletion" 73 href="<?php echo esc_url(Moderation_Api_Admin::get_page_url('delete_key')); ?>"><?php esc_html_e('Disconnect this account', 'modapi'); ?></a> 31 74 </div> 32 75 33 <div class="inside"> 34 <table class="modapi-account"> 35 <tbody> 36 <tr> 37 <th scope="row"><?php esc_html_e( 'Project', 'modapi' ); ?></th> 38 <td> 39 <?php echo esc_html( $modapi_user->current_project->name ); ?> 40 </td> 41 </tr> 42 43 <tr> 44 <th scope="row"><?php esc_html_e( 'Subscription type', 'modapi' ); ?></th> 45 <td> 46 <?php echo esc_html( $modapi_user->paid_plan_name ); ?> 47 </td> 48 </tr> 49 50 51 <tr> 52 <th scope="row"><?php esc_html_e( 'Quota', 'modapi' ); ?></th> 53 <td> 54 <?php echo number_format(esc_html( $modapi_user->text_api_quota )); ?> 55 </td> 56 </tr> 57 58 <tr> 59 <th scope="row"><?php esc_html_e( 'Remaining quota', 'modapi' ); ?></th> 60 <td> 61 <?php echo number_format(esc_html( $modapi_user->remaining_quota )); ?> 62 </td> 63 </tr> 64 65 66 </tbody> 67 </table> 68 <div class="modapi-settings__row"></div> 69 <div class="modapi-card-actions"> 70 <div id="delete-action"> 71 <a class="submitdelete deletion" href="<?php echo esc_url( Moderation_Api_Admin::get_page_url( 'delete_key' ) ); ?>"><?php esc_html_e( 'Disconnect this account', 'modapi' ); ?></a> 72 </div> 73 74 <div id="publishing-action"> 75 <a href="https://moderationapi.com/app/upgrade" target="_blank"> 76 <button type="button" class="modapi-button " > 77 <?php esc_attr_e( 'Upgrade plan', 'modapi' ); ?> 78 </button> 79 </a> 80 </div> 81 <div class="clear"></div> 82 </div> 76 <div id="publishing-action"> 77 <a href="https://moderationapi.com/app/upgrade" target="_blank"> 78 <button type="button" class="modapi-button "> 79 <?php esc_attr_e('Upgrade plan', 'modapi'); ?> 80 </button> 81 </a> 83 82 </div> 84 83 </div> 85 84 </div> 85 </div> 86 87 <?php if (is_multisite()): ?> 88 <?php 89 $network_api_key = get_site_option('moderation_api_network_key'); 90 $enforce_network = get_site_option('modapi_enforce_network', '0'); 91 $can_override = Moderation_Api_Network_Admin::can_override_network_settings(); 92 $using_network_key = !get_option('moderation_api_key') && $network_api_key; 93 ?> 86 94 <div class="modapi-card"> 87 95 <div class="modapi-section-header"> 88 96 <h2 class="modapi-section-header__label"> 89 <span><?php esc_html_e( 'Settings', 'modapi'); ?></span>97 <span><?php esc_html_e('Multisite Settings', 'modapi'); ?></span> 90 98 </h2> 91 99 </div> 92 100 93 101 <div class="inside"> 94 <form action="<?php echo esc_url( Moderation_Api_Admin::get_page_url() ); ?>" autocomplete="off" method="POST" id="modapi-settings-form"> 95 102 <div class="modapi-settings"> 103 104 105 <div class="modapi-settings__row"> 106 <h3 class="modapi-settings__row-title"> 107 <label 108 class="modapi-settings__row-label"><?php esc_html_e('Site Moderation', 'modapi'); ?></label> 109 </h3> 110 <div style="margin-bottom: 10px;"> 111 <?php 112 $site_disabled = get_option('modapi_site_disabled', '0'); 113 $network_enforced = $enforce_network === '1'; 114 ?> 115 <?php if ($network_enforced): ?> 116 <span 117 style="background: #646970; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 118 <?php esc_html_e('NETWORK CONTROLLED', 'modapi'); ?> 119 </span> 120 <p style="color: #646970; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 121 <?php esc_html_e('Moderation cannot be disabled when network settings are enforced.', 'modapi'); ?> 122 </p> 123 <?php else: ?> 124 <?php if ($site_disabled === '1'): ?> 125 <span 126 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 127 <?php esc_html_e('MODERATION DISABLED', 'modapi'); ?> 128 </span> 129 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 130 <?php esc_html_e('Comments will not be analyzed by Moderation API on this site.', 'modapi'); ?> 131 </p> 132 <?php else: ?> 133 134 <p style="font-size: 13px; margin: 5px 0 0 0; "> 135 <?php esc_html_e('Disable moderation for this site only.', 'modapi'); ?> 136 </p> 137 <?php endif; ?> 138 <?php endif; ?> 139 </div> 140 <div class="modapi-settings__row-input"> 141 <?php if (!$network_enforced): ?> 142 <form method="post" style="display: flex; flex-direction: column; align-items: flex-start;"> 143 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 144 <input type="hidden" name="action" value="toggle-site-moderation"> 145 <?php if ($site_disabled === '1'): ?> 146 <input type="submit" name="submit" 147 value="<?php esc_attr_e('Enable Moderation', 'modapi'); ?>" 148 class=" modapi-button secondary"> 149 <?php else: ?> 150 <input type="submit" name="submit" 151 value="<?php esc_attr_e('Disable Moderation For This Site', 'modapi'); ?>" 152 class=" modapi-button secondary"> 153 <?php endif; ?> 154 </form> 155 <?php endif; ?> 156 </div> 157 </div> 158 159 <?php if (current_user_can('manage_network_options')): ?> 160 <div class="modapi-settings__row"> 161 <h3 class="modapi-settings__row-title"> 162 <label 163 class="modapi-settings__row-label"><?php esc_html_e('Network Admin', 'modapi'); ?></label> 164 </h3> 165 <div class="modapi-settings__row-input"> 166 <a href="<?php echo esc_url(network_admin_url('settings.php?page=moderation-api-network')); ?>" 167 class=" modapi-button secondary"> 168 <?php esc_html_e('Manage Network Settings', 'modapi'); ?> 169 </a> 170 </div> 171 </div> 172 <?php endif; ?> 173 </div> 174 </div> 175 </div> 176 <?php endif; ?> 177 178 <?php 179 // Ensure $site_disabled is always defined 180 if (!isset($site_disabled)) { 181 $site_disabled = get_option('modapi_site_disabled', '0'); 182 } 183 ?> 184 185 <?php if ($site_disabled !== '1'): ?> 186 187 <div class="modapi-card"> 188 <div class="modapi-section-header"> 189 <h2 class="modapi-section-header__label"> 190 <span><?php esc_html_e('API Configuration', 'modapi'); ?></span> 191 </h2> 192 </div> 193 194 <div class="inside"> 195 <form action="<?php echo esc_url(Moderation_Api_Admin::get_page_url()); ?>" autocomplete="off" 196 method="POST" id="modapi-settings-form"> 197 96 198 <div class="modapi-settings"> 97 <div class="modapi-settings__row"> 199 <div class="modapi-settings__row"> 200 <?php 201 $site_api_key = get_option('moderation_api_key'); 202 $network_api_key = get_site_option('moderation_api_network_key'); 203 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 204 $using_site_key = !empty($site_api_key) && !$network_enforced; 205 $using_network_key = !$using_site_key && !empty($network_api_key); 206 ?> 207 <h3 class="modapi-settings__row-title"> 208 <label class="modapi-settings__row-label" 209 for="key"><?php esc_html_e('API key', 'modapi'); ?></label> 210 </h3> 211 <?php if (is_multisite() && ($using_site_key || $using_network_key || $network_enforced)): ?> 212 <div style="margin-bottom: 10px;"> 213 <?php if ($network_enforced): ?> 214 <span 215 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 216 <?php esc_html_e('NETWORK ENFORCED', 'modapi'); ?> 217 </span> 218 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 219 <?php esc_html_e('This API key is enforced by network settings and cannot be changed at the site level.', 'modapi'); ?> 220 </p> 221 <?php elseif ($using_site_key): ?> 222 <span 223 style="background: #dba617; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 224 <?php esc_html_e('SITE OVERRIDE', 'modapi'); ?> 225 </span> 226 <p style="color: #dba617; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 227 <?php esc_html_e('This site is using a custom API key that overrides the network default.', 'modapi'); ?> 228 </p> 229 <?php elseif ($using_network_key): ?> 230 <span 231 style="background: #00a32a; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 232 <?php esc_html_e('NETWORK DEFAULT', 'modapi'); ?> 233 </span> 234 <p style="color: #00a32a; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 235 <?php esc_html_e('This site is using the network-wide API key.', 'modapi'); ?> 236 </p> 237 <?php endif; ?> 238 </div> 239 <?php endif; ?> 240 <div class="modapi-settings__row-input"> 241 <?php 242 $site_api_key = get_option('moderation_api_key'); 243 $network_api_key = get_site_option('moderation_api_network_key'); 244 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 245 $using_site_key = !empty($site_api_key) && !$network_enforced; 246 $using_network_key = !$using_site_key && !empty($network_api_key); 247 248 if ($network_enforced) { 249 $api_key_value = $network_api_key; 250 } else if ($using_site_key) { 251 $api_key_value = $site_api_key; 252 } else { 253 $api_key_value = $network_api_key ?: ''; 254 } 255 ?> 256 257 <span class="api-key"> 258 <input id="key" name="key" type="text" style="width: 100%;" 259 value="<?php echo esc_attr($api_key_value); ?>" <?php echo $network_enforced ? 'readonly disabled' : ''; ?>> 260 </span> 261 262 263 </div> 264 </div> 265 266 </div> 267 268 <?php if (!$network_enforced): ?> 269 <div class="modapi-card-actions"> 270 271 272 273 <div style="flex: 1;"></div> 274 275 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 276 277 <div id="publishing-action"> 278 <input type="hidden" name="action" value="enter-key"> 279 <input type="submit" name="submit" id="submit" class="modapi-button modapi-could-be-primary" 280 value="<?php esc_attr_e('Save API Key', 'modapi'); ?>"> 281 </div> 282 </div> 283 <?php endif; ?> 284 </form> 285 286 287 <?php if ($using_site_key && !empty($network_api_key)): ?> 288 <div class="modapi-card-actions-extra"> 289 <form method="post" style="margin-top: 10px;"> 290 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 291 <input type="hidden" name="action" value="use-network-api-key"> 292 <input type="submit" name="submit" value="<?php esc_attr_e('Use Network API Key', 'modapi'); ?>" 293 class="modapi-button secondary" style="font-size: 12px; padding: 4px 8px;"> 294 </form> 295 </div> 296 <?php endif; ?> 297 298 299 </div> 300 </div> 301 302 <div class="modapi-card"> 303 <div class="modapi-section-header"> 304 <h2 class="modapi-section-header__label"> 305 <span><?php esc_html_e('Comment Moderation', 'modapi'); ?></span> 306 </h2> 307 </div> 308 309 <div class="inside"> 310 <div class="modapi-settings"> 311 <div class="modapi-settings__row"> 312 <h3 class="modapi-settings__row-title"> 313 <label 314 class="modapi-settings__row-label"><?php esc_html_e('Content filter', 'modapi'); ?></label> 315 </h3> 316 <div class="modapi-settings__row-input"> 317 <a href="<?php echo esc_url(Moderation_Api::API_URL . '/app/projects/' . esc_attr($modapi_user->current_project->id)); ?>" 318 target="_blank"> 319 <button type="button" class="modapi-button secondary"> 320 <?php esc_attr_e('Edit content filter', 'modapi'); ?> 321 </button> 322 </a> 323 </div> 324 </div> 325 </div> 326 327 <form action="<?php echo esc_url(Moderation_Api_Admin::get_page_url()); ?>" autocomplete="off" 328 method="POST" id="modapi-flagged-comments-form"> 329 <div class="modapi-settings"> 330 <div class="modapi-settings__row is-radio"> 331 <?php 332 $site_flagged_action = get_option('modapi_flagged_action'); 333 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 334 $effective_action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action'); 335 $radio_disabled = $network_enforced ? 'disabled' : ''; 336 337 // More precise logic for determining setting source 338 $has_site_flagged = ($site_flagged_action !== false && $site_flagged_action !== ''); 339 $has_network_flagged = ($network_flagged_action !== false && $network_flagged_action !== ''); 340 $using_site_flagged = $has_site_flagged && !$network_enforced; 341 $using_network_flagged = !$has_site_flagged && $has_network_flagged && !$network_enforced; 342 343 // Debug info (remove in production) 344 if (WP_DEBUG) { 345 echo "<!-- DEBUG: site_flagged_action: " . var_export($site_flagged_action, true) . " -->"; 346 echo "<!-- DEBUG: network_flagged_action: " . var_export($network_flagged_action, true) . " -->"; 347 echo "<!-- DEBUG: has_site_flagged: " . var_export($has_site_flagged, true) . " -->"; 348 echo "<!-- DEBUG: has_network_flagged: " . var_export($has_network_flagged, true) . " -->"; 349 echo "<!-- DEBUG: using_site_flagged: " . var_export($using_site_flagged, true) . " -->"; 350 echo "<!-- DEBUG: network_enforced: " . var_export($network_enforced, true) . " -->"; 351 } 352 ?> 353 <div class="modapi-settings__row-text"> 98 354 <h3 class="modapi-settings__row-title"> 99 < label class="modapi-settings__row-label" for="key"><?php esc_html_e( 'API key', 'modapi' ); ?></label>355 <?php esc_html_e('Flagged comments', 'modapi'); ?> 100 356 </h3> 101 <div class="modapi-settings__row-input"> 102 <span class="api-key"><input id="key" name="key" type="text" style="width: 100%;" value="<?php echo esc_attr( get_option('moderation_api_key') ); ?>" ></span> 103 </div> 104 </div> 105 106 107 <div class="modapi-settings__row"> 108 <h3 class="modapi-settings__row-title"> 109 <label class="modapi-settings__row-label" for="key"><?php esc_html_e( 'Content filter', 'modapi' ); ?></label> 110 </h3> 111 <div class="modapi-settings__row-input"> 112 <a href="<?php echo esc_url(Moderation_Api::API_URL . '/app/projects/' . esc_attr($modapi_user->current_project->id)); ?>" target="_blank"> 113 <button type="button" class="modapi-button secondary" > 114 <?php esc_attr_e( 'Edit content filter', 'modapi' ); ?> 115 </button> 116 </a> 117 </div> 118 119 </div> 120 121 122 123 <div class="modapi-settings__row is-radio"> 124 <div class="modapi-settings__row-text"> 125 <h3 class="modapi-settings__row-title"><?php esc_html_e( 'Flagged comments', 'modapi' ); ?></h3> 357 <?php if (is_multisite() && ($using_site_flagged || $using_network_flagged || $network_enforced)): ?> 358 <div style="margin-bottom: 15px;"> 359 <?php if ($network_enforced): ?> 360 <span 361 style="background: #d63638; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 362 <?php esc_html_e('NETWORK ENFORCED', 'modapi'); ?> 363 </span> 364 <p style="color: #d63638; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 365 <?php esc_html_e('These settings are enforced by network configuration and cannot be changed at the site level.', 'modapi'); ?> 366 </p> 367 <?php elseif ($using_site_flagged): ?> 368 <span 369 style="background: #dba617; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 370 <?php esc_html_e('SITE OVERRIDE', 'modapi'); ?> 371 </span> 372 <p style="color: #dba617; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 373 <?php esc_html_e('This site is using custom flagged comment behavior that overrides the network default.', 'modapi'); ?> 374 </p> 375 <?php elseif ($using_network_flagged): ?> 376 <span 377 style="background: #00a32a; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: 600;"> 378 <?php esc_html_e('NETWORK DEFAULT', 'modapi'); ?> 379 </span> 380 <p style="color: #00a32a; font-size: 13px; margin: 5px 0 0 0; font-weight: 500;"> 381 <?php esc_html_e('This site is using the network-wide flagged comment behavior.', 'modapi'); ?> 382 </p> 383 <?php endif; ?> 384 </div> 385 <?php endif; ?> 126 386 </div> 127 387 <div class="modapi-settings__row-input"> 128 <fieldset> 129 388 <?php 389 $site_flagged_action = get_option('modapi_flagged_action'); 390 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 391 $effective_action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action'); 392 $radio_disabled = $network_enforced ? 'disabled' : ''; 393 394 // More precise logic for determining setting source 395 $has_site_flagged = ($site_flagged_action !== false && $site_flagged_action !== ''); 396 $has_network_flagged = ($network_flagged_action !== false && $network_flagged_action !== ''); 397 $using_site_flagged = $has_site_flagged && !$network_enforced; 398 $using_network_flagged = !$has_site_flagged && $has_network_flagged && !$network_enforced; 399 ?> 400 <fieldset <?php echo $network_enforced ? 'disabled' : ''; ?>> 401 130 402 <div> 131 403 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_3"> 132 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_3" value="3" <?php checked( '3', get_option( 'modapi_flagged_action' ) ); ?> /> 404 <input type="radio" name="modapi_flagged_action" 405 id="modapi_flagged_action_3" value="3" <?php checked('3', $effective_action); ?> <?php echo $radio_disabled; ?> /> 133 406 <span class="modapi-settings__row-label-text"> 134 <?php esc_html_e( 'Move flagged comments to spam.', 'modapi'); ?>407 <?php esc_html_e('Move flagged comments to spam.', 'modapi'); ?> 135 408 </span> 136 409 </label> … … 138 411 <div> 139 412 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_2"> 140 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_2" value="2" <?php checked( '2', get_option( 'modapi_flagged_action' ) ); ?> /> 413 <input type="radio" name="modapi_flagged_action" 414 id="modapi_flagged_action_2" value="2" <?php checked('2', $effective_action); ?> <?php echo $radio_disabled; ?> /> 141 415 <span class="modapi-settings__row-label-text"> 142 <?php esc_html_e( 'Move flagged comments to pending for review.', 'modapi'); ?>416 <?php esc_html_e('Move flagged comments to pending for review.', 'modapi'); ?> 143 417 </span> 144 418 </label> … … 147 421 <div> 148 422 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_4"> 149 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_4" value="4" <?php checked( '4', get_option( 'modapi_flagged_action' ) ); ?> /> 423 <input type="radio" name="modapi_flagged_action" 424 id="modapi_flagged_action_4" value="4" <?php checked('4', $effective_action); ?> <?php echo $radio_disabled; ?> /> 150 425 <span class="modapi-settings__row-label-text"> 151 <?php esc_html_e( 'Move flagged comments to trash.', 'modapi'); ?>426 <?php esc_html_e('Move flagged comments to trash.', 'modapi'); ?> 152 427 </span> 153 428 </label> 154 429 </div> 155 430 156 431 <div> 157 432 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_1"> 158 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_1" value="1" <?php checked( '1', get_option( 'modapi_flagged_action' ) ); ?> /> 433 <input type="radio" name="modapi_flagged_action" 434 id="modapi_flagged_action_1" value="1" <?php checked('1', $effective_action); ?> <?php echo $radio_disabled; ?> /> 159 435 <span class="modapi-settings__row-label-text"> 160 <?php esc_html_e( 'Move flagged comments to approved.', 'modapi'); ?>436 <?php esc_html_e('Move flagged comments to approved.', 'modapi'); ?> 161 437 </span> 162 438 </label> … … 164 440 <div> 165 441 <label class="modapi-settings__row-input-label" for="modapi_flagged_action_0"> 166 <input type="radio" name="modapi_flagged_action" id="modapi_flagged_action_0" value="0" <?php checked( '0', get_option( 'modapi_flagged_action' ) ); ?> /> 442 <input type="radio" name="modapi_flagged_action" 443 id="modapi_flagged_action_0" value="0" <?php checked('0', $effective_action); ?> <?php echo $radio_disabled; ?> /> 167 444 <span class="modapi-settings__row-label-text"> 168 <?php esc_html_e( 'Do not do anything.', 'modapi'); ?>445 <?php esc_html_e('Do not do anything.', 'modapi'); ?> 169 446 </span> 170 447 </label> … … 174 451 175 452 <div class="modapi-settings__row-note"> 176 <strong><?php esc_html_e( 'Note:', 'modapi' ); ?></strong> 177 We recommend to use the <a href="https://moderationapi.com/app/moderation/queue" target="_blank">content queue</a> in the Moderation API dashboard to review flagged comments. 453 <strong><?php esc_html_e('Note:', 'modapi'); ?></strong> 454 We recommend to use the <a href="https://moderationapi.com/app/moderation/queue" 455 target="_blank">content queue</a> in the Moderation API dashboard to review 456 flagged comments. 178 457 </div> 179 458 180 459 </div> 181 460 </div> 182 183 184 461 </div> 185 186 <div class="modapi-card-actions"> 187 188 189 <?php wp_nonce_field( Moderation_Api_Admin::NONCE ); ?> 190 191 <div id="publishing-action"> 192 <input type="hidden" name="action" value="enter-key"> 193 <input type="submit" name="submit" id="submit" class="modapi-button modapi-could-be-primary" value="<?php esc_attr_e( 'Save changes', 'modapi' ); ?>"> 194 </div> 195 <div class="clear"></div> 462 463 <?php if (!$network_enforced): ?> 464 <div class="modapi-card-actions"> 465 466 467 <div style="flex: 1;"></div> 468 469 470 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 471 472 <div id="publishing-action"> 473 <input type="hidden" name="action" value="update-flagged-action"> 474 <input type="submit" name="submit" id="submit-flagged" 475 class="modapi-button modapi-could-be-primary" 476 value="<?php esc_attr_e('Save', 'modapi'); ?>"> 477 </div> 478 </div> 479 <?php endif; ?> 480 </form> 481 482 483 <?php if (is_multisite() && $using_site_flagged && $has_network_flagged && !$network_enforced): ?> 484 <div class="modapi-card-actions-extra"> 485 <form method="post" class=""> 486 <?php wp_nonce_field(Moderation_Api_Admin::NONCE); ?> 487 <input type="hidden" name="action" value="use-network-flagged-action"> 488 <input type="submit" name="submit" value="<?php esc_attr_e('Use Network Setting', 'modapi'); ?>" 489 class="modapi-button secondary" style="font-size: 12px; padding: 4px 8px;"> 490 </form> 491 492 493 </form> 494 <span style="margin-left: 10px; color: #666; font-size: 12px;"> 495 <?php esc_html_e('Remove site override and use network default', 'modapi'); ?> 496 </span> 196 497 </div> 197 </form> 498 <?php endif; ?> 499 500 198 501 </div> 199 502 </div> 200 503 201 504 <?php endif; // End if site is not disabled ?> 505 202 506 </div> 203 507 </div> -
moderation-api-automated-content-moderation/trunk/development.md
r3177384 r3326772 13 13 npx @wordpress/env start 14 14 ``` 15 16 ## Admin credentials 17 18 admin 19 password -
moderation-api-automated-content-moderation/trunk/includes/class-moderation-api-activator.php
r3143763 r3326772 31 31 */ 32 32 public static function activate() { 33 // Initialize default flagged action setting if not already set 34 if (get_option('modapi_flagged_action') === false) { 35 add_option('modapi_flagged_action', '2'); 36 } 37 } 33 38 39 /** 40 * Network activation - Initialize default settings for new sites 41 * 42 * @since 1.0.3 43 */ 44 public static function network_activate() { 45 // Don't create local flagged action setting for new sites 46 // This allows them to inherit the network setting through get_effective_flagged_action() 47 48 // Only create the option if there's no network setting at all 49 $network_flagged_action = get_site_option('modapi_network_flagged_action'); 50 if ($network_flagged_action === false || $network_flagged_action === '') { 51 // No network setting exists, create a local default 52 if (get_option('modapi_flagged_action') === false) { 53 add_option('modapi_flagged_action', '2'); 54 } 55 } 56 // Otherwise, leave no local option so the site inherits network settings 34 57 } 35 58 -
moderation-api-automated-content-moderation/trunk/includes/class-moderation-api.php
r3177384 r3326772 28 28 * @author Moderation API <[email protected]> 29 29 */ 30 class Moderation_Api { 30 class Moderation_Api 31 { 31 32 32 33 /** … … 70 71 * @since 1.0.0 71 72 */ 72 public function __construct() { 73 if ( defined( 'MODERATION_API_VERSION' ) ) { 73 public function __construct() 74 { 75 if (defined('MODERATION_API_VERSION')) { 74 76 $this->version = MODERATION_API_VERSION; 75 77 } else { … … 83 85 $this->define_public_hooks(); 84 86 87 if (is_multisite()) { 88 $this->define_network_admin_hooks(); 89 } 90 85 91 } 86 92 … … 101 107 * @access private 102 108 */ 103 private function load_dependencies() { 109 private function load_dependencies() 110 { 104 111 105 112 /** … … 107 114 * core plugin. 108 115 */ 109 require_once plugin_dir_path( dirname( __FILE__ )) . 'includes/class-moderation-api-loader.php';116 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-moderation-api-loader.php'; 110 117 111 118 /** … … 113 120 * of the plugin. 114 121 */ 115 require_once plugin_dir_path( dirname( __FILE__ )) . 'includes/class-moderation-api-i18n.php';122 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-moderation-api-i18n.php'; 116 123 117 124 /** 118 125 * The class responsible for defining all actions that occur in the admin area. 119 126 */ 120 require_once plugin_dir_path( dirname( __FILE__ )) . 'admin/class-moderation-api-admin.php';127 require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-moderation-api-admin.php'; 121 128 122 129 /** … … 124 131 * side of the site. 125 132 */ 126 require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-moderation-api-public.php'; 133 require_once plugin_dir_path(dirname(__FILE__)) . 'public/class-moderation-api-public.php'; 134 135 /** 136 * The class responsible for defining network admin functionality in multisite. 137 */ 138 if (is_multisite()) { 139 require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-moderation-api-network-admin.php'; 140 } 127 141 128 142 $this->loader = new Moderation_Api_Loader(); … … 139 153 * @access private 140 154 */ 141 private function set_locale() { 155 private function set_locale() 156 { 142 157 143 158 $plugin_i18n = new Moderation_Api_i18n(); 144 159 145 $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');160 $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain'); 146 161 147 162 } … … 154 169 * @access private 155 170 */ 156 private function define_admin_hooks() { 157 158 $plugin_admin = new Moderation_Api_Admin( $this->get_plugin_name(), $this->get_version() ); 159 160 $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); 161 $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); 171 private function define_admin_hooks() 172 { 173 174 $plugin_admin = new Moderation_Api_Admin($this->get_plugin_name(), $this->get_version()); 175 176 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles'); 177 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts'); 162 178 163 179 // add admin sidepanel 164 $this->loader->add_action( 'admin_menu', $plugin_admin, 'add_admin_menu');180 $this->loader->add_action('admin_menu', $plugin_admin, 'add_admin_menu'); 165 181 166 182 // hook for admin settings page 167 $this->loader->add_action( 'admin_init', $plugin_admin, 'init');168 169 170 $this->loader->add_filter( 'manage_edit-comments_columns', $plugin_admin, 'custom_comments_column');171 $this->loader->add_action( 'manage_comments_custom_column', $plugin_admin, 'show_flagged_custom_field', 10, 2);183 $this->loader->add_action('admin_init', $plugin_admin, 'init'); 184 185 186 $this->loader->add_filter('manage_edit-comments_columns', $plugin_admin, 'custom_comments_column'); 187 $this->loader->add_action('manage_comments_custom_column', $plugin_admin, 'show_flagged_custom_field', 10, 2); 172 188 173 189 … … 177 193 // Add a custom column to the comments list table 178 194 $this->loader->add_filter('manage_edit-comments_columns', $plugin_admin, 'add_modapi_error_column'); 179 180 $this->loader->add_action( 'admin_notices', $this, 'add_admin_notices');195 196 $this->loader->add_action('admin_notices', $this, 'add_admin_notices'); 181 197 182 198 } … … 191 207 * @access private 192 208 */ 193 private function define_public_hooks() { 194 195 $plugin_public = new Moderation_Api_Public( $this->get_plugin_name(), $this->get_version() ); 196 197 $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); 198 $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); 209 private function define_public_hooks() 210 { 211 212 $plugin_public = new Moderation_Api_Public($this->get_plugin_name(), $this->get_version()); 213 214 $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_styles'); 215 $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_scripts'); 199 216 200 217 $this->loader->add_action('comment_post', $plugin_public, 'moderation_api_comment_post', 10, 2); … … 207 224 } 208 225 209 210 211 226 /** 227 * Register all of the hooks related to the network admin functionality 228 * of the plugin. 229 * 230 * @since 1.0.3 231 * @access private 232 */ 233 private function define_network_admin_hooks() 234 { 235 236 $plugin_network_admin = new Moderation_Api_Network_Admin($this->get_plugin_name(), $this->get_version()); 237 238 $this->loader->add_action('network_admin_enqueue_scripts', $plugin_network_admin, 'enqueue_styles'); 239 $this->loader->add_action('network_admin_enqueue_scripts', $plugin_network_admin, 'enqueue_scripts'); 240 241 // Add network admin menu 242 $this->loader->add_action('network_admin_menu', $plugin_network_admin, 'add_network_admin_menu'); 243 244 // Hook for network admin settings page processing 245 $this->loader->add_action('admin_init', $plugin_network_admin, 'init'); 246 $this->loader->add_action('network_admin_init', $plugin_network_admin, 'init'); 247 } 248 249 250 251 212 252 213 253 /** … … 216 256 * @since 1.0.0 217 257 */ 218 public function run() { 258 public function run() 259 { 219 260 $this->loader->run(); 220 261 } … … 227 268 * @return string The name of the plugin. 228 269 */ 229 public function get_plugin_name() { 270 public function get_plugin_name() 271 { 230 272 return $this->plugin_name; 231 273 } … … 237 279 * @return Moderation_Api_Loader Orchestrates the hooks of the plugin. 238 280 */ 239 public function get_loader() { 281 public function get_loader() 282 { 240 283 return $this->loader; 241 284 } … … 247 290 * @return string The version number of the plugin. 248 291 */ 249 public function get_version() { 292 public function get_version() 293 { 250 294 return $this->version; 251 295 } 252 296 253 297 254 public static function view( $name, array $args = array() ) { 255 $args = apply_filters( 'moderation_api_view_arguments', $args, $name ); 256 257 foreach ( $args AS $key => $val ) { 298 public static function view($name, array $args = array()) 299 { 300 $args = apply_filters('moderation_api_view_arguments', $args, $name); 301 302 foreach ($args as $key => $val) { 258 303 $$key = $val; 259 304 } 260 305 261 306 // load_plugin_textdomain( 'modapi' ); 262 307 263 $file = MODERATION_API_PLUGIN_DIR . 'admin/views/'. $name . '.php'; 264 265 include( $file ); 266 } 267 268 public static function get_api_key(){ 269 return get_option( 'moderation_api_key' ); 270 } 271 272 273 public static function display_status() { 274 275 } 276 277 278 public static function get_webhook_url() { 279 return home_url( '/moderation-api-webhook' ); 308 $file = MODERATION_API_PLUGIN_DIR . 'admin/views/' . $name . '.php'; 309 310 include($file); 311 } 312 313 public static function get_api_key() 314 { 315 if (is_multisite()) { 316 return Moderation_Api_Network_Admin::get_effective_api_key(); 317 } 318 return get_option('moderation_api_key'); 319 } 320 321 /** 322 * Check if moderation is disabled for the current site 323 */ 324 public static function is_moderation_disabled() 325 { 326 $site_disabled = get_option('modapi_site_disabled', '0') === '1'; 327 $network_enforced = is_multisite() && get_site_option('modapi_enforce_network', '0') === '1'; 328 329 // Moderation is disabled if the site has it disabled AND network isn't enforced 330 return $site_disabled && !$network_enforced; 331 } 332 333 334 public static function display_status() 335 { 336 337 } 338 339 340 public static function get_webhook_url() 341 { 342 return home_url('/moderation-api-webhook'); 280 343 } 281 344 … … 283 346 * Display admin notices on the Comments page. 284 347 */ 285 public function add_admin_notices() { 348 public function add_admin_notices() 349 { 286 350 // Get the current screen 287 351 $screen = get_current_screen(); 288 352 289 353 // Check if we are on the Comments admin page 290 if ( 'edit-comments' === $screen->id) {354 if ('edit-comments' === $screen->id) { 291 355 $api_key = Moderation_Api::get_api_key(); 292 356 293 357 if (!$api_key) { 294 Moderation_Api::view('notice', array(295 'message' => __( 'Activate your Moderation API account to protect your site.', 'moderation-api'),296 'type'=> 'info', // Types: 'success', 'error', 'warning', 'info'297 ) );358 Moderation_Api::view('notice', array( 359 'message' => __('Activate your Moderation API account to protect your site.', 'moderation-api'), 360 'type' => 'info', // Types: 'success', 'error', 'warning', 'info' 361 )); 298 362 } 299 363 } -
moderation-api-automated-content-moderation/trunk/moderation-api.php
r3177384 r3326772 17 17 * Plugin URI: https://moderationapi.com/integrations/wordpress-content-moderation 18 18 * Description: Use Moderation API to automatically moderate comments on your WordPress site. Detects a large range of contet such as bullying, discrimination, sentiment, NSFW, PII, and much more. 19 * Version: 1.0. 219 * Version: 1.0.3 20 20 * Author: Moderation API 21 21 * Author URI: https://moderationapi.com/ … … 36 36 * Rename this for your plugin and update it as you release new versions. 37 37 */ 38 define( 'MODERATION_API_VERSION', '1.0. 2' );38 define( 'MODERATION_API_VERSION', '1.0.3' ); 39 39 40 40 define( 'MODERATION_API_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); … … 62 62 63 63 /** 64 * The code that runs during plugin network activation for new sites. 65 */ 66 function moderation_api_network_activate() { 67 require_once plugin_dir_path( __FILE__ ) . 'includes/class-moderation-api-activator.php'; 68 Moderation_API_Activator::network_activate(); 69 } 70 71 if (is_multisite()) { 72 add_action('wpmu_new_blog', 'moderation_api_network_activate'); 73 } 74 75 /** 64 76 * The core plugin class that is used to define internationalization, 65 77 * admin-specific hooks, and public-facing site hooks. -
moderation-api-automated-content-moderation/trunk/public/class-moderation-api-public.php
r3177384 r3326772 24 24 25 25 /** 26 * Static array to track processed comments to prevent duplicate API calls 27 * 28 * @since 1.0.0 29 * @access private 30 * @var array $processed_comments Array of comment hashes already processed 31 */ 32 private static $processed_comments = array(); 33 34 /** 26 35 * The ID of this plugin. 27 36 * … … 53 62 $this->version = $version; 54 63 55 // Add the pre-approval filter with priority 20 (after default filters)56 64 add_filter('pre_comment_approved', array($this, 'pre_comment_approved_filter'), 20, 2); 57 65 add_action('edit_comment', array($this, 'edit_comment_handler'), 10, 2); 58 66 } 59 67 … … 105 113 106 114 107 public function analyze_comment($comment ){115 public function analyze_comment($comment, $content_id = null){ 108 116 $api_key = Moderation_Api::get_api_key(); 109 117 … … 113 121 $user_email = sanitize_email($comment->comment_author_email); 114 122 $user_ip = sanitize_text_field($comment->comment_author_IP); 115 // $comment_id = absint($comment->comment_ID);116 123 $post_id = absint($comment->comment_post_ID); 117 124 $post_url = esc_url(get_permalink($post_id)); … … 124 131 $authorId = $user_ip; 125 132 } 133 134 // Predict comment ID for URL metadata 135 $last_comment = get_comments(array('number' => 1, 'orderby' => 'comment_ID', 'order' => 'DESC')); 136 $predicted_comment_id = $last_comment ? $last_comment[0]->comment_ID + 1 : 1; 126 137 127 138 $url = Moderation_Api::API_URL . "/api/v1/moderate/text"; … … 135 146 'contextId' => (string) $post_id, 136 147 'metadata' => array( 137 // 'comment_id' => $comment_id, 138 'url' => $post_url 148 'url' => $post_url . '#comment-' . $predicted_comment_id 139 149 ) 140 150 ); 151 152 // If content_id is provided (for edits), include it to maintain history 153 if ($content_id) { 154 $data['contentId'] = $content_id; 155 } 141 156 142 157 $response = wp_remote_post($url, array( … … 149 164 // Decode the JSON response 150 165 $responseData = json_decode($body, true); 166 167 // Keep contentId from API response as-is 168 151 169 // Return the response data 152 170 return $responseData; … … 179 197 $json = json_decode($raw_data, true); 180 198 $type = isset($json['type']) ? sanitize_text_field($json['type']) : ''; 181 $metadata = isset($json['metadata']) ? $json['metadata'] : array(); 182 $comment_id = isset($metadata['comment_id']) ? absint($metadata['comment_id']) : 0; 183 184 if($type == 'QUEUE_ITEM_ACTION' && $comment_id ){ 199 $item_id = isset($json['item']['id']) ? sanitize_text_field($json['item']['id']) : ''; 200 201 if($type == 'QUEUE_ITEM_ACTION' && $item_id) { 202 // Find comment by content ID (multisite compatible) 203 $result = $this->find_comment_by_content_id($item_id); 204 205 if (!$result) { 206 wp_send_json_error('Comment not found for item ID: ' . $item_id, 404); 207 exit(); 208 } 209 210 $comment_id = $result['comment_id']; 211 $site_id = $result['site_id']; 212 185 213 // Sanitize and validate the action 186 214 $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : ''; … … 191 219 } 192 220 193 $comment = get_comment($comment_id); 194 195 if(!$comment){ 196 wp_send_json_error('Comment not found', 404); 197 exit(); 221 // Switch to the correct site if multisite 222 if (is_multisite() && $site_id) { 223 switch_to_blog($site_id); 198 224 } 199 225 … … 213 239 } 214 240 241 // Restore original blog if we switched 242 if (is_multisite() && $site_id) { 243 restore_current_blog(); 244 } 245 215 246 wp_send_json_success($action); 216 247 exit(); … … 233 264 public function pre_comment_approved_filter($approved, $commentdata) { 234 265 try { 266 // Check if site moderation is disabled 267 if (Moderation_Api::is_moderation_disabled()) { 268 // Moderation is disabled for this site, skip analysis 269 return $approved; 270 } 271 235 272 // Convert commentdata array to object for consistency 236 273 $comment = (object) $commentdata; 237 238 // Analyze the comment 239 $analysis = $this->analyze_comment($comment); 240 241 if ($analysis === false) { 274 275 // Create unique hash for this comment to prevent duplicate API calls 276 $comment_hash = md5($comment->comment_content . $comment->comment_author_IP . $comment->comment_post_ID); 277 278 // Generate cache key for analysis results 279 $cache_key = 'modapi_analysis_' . md5($comment->comment_content . $comment->comment_author_IP); 280 281 // Check if we've already processed this comment 282 if (in_array($comment_hash, self::$processed_comments)) { 283 error_log("MOD_API Comment already processed - using cached result"); 284 $analysis = wp_cache_get($cache_key); 285 } else { 286 // Mark this comment as processed 287 self::$processed_comments[] = $comment_hash; 288 error_log("MOD_API Processing comment: " . $comment->comment_content); 289 290 $analysis = $this->analyze_comment($comment); 291 292 if ($analysis === false) { 242 293 // On API failure, let other filters handle it 243 294 return $approved; 244 } 245 246 // Store analysis results in temporary location 247 wp_cache_set('modapi_analysis_' . $comment->comment_author_IP, $analysis, '', 60); 248 249 250 if ($analysis['flagged']) { 251 $action = get_option('modapi_flagged_action', '0'); 295 } 252 296 253 switch ($action) { 254 case '1': 255 // Let comment through 256 return '1'; 257 258 case '2': 259 // Hold for moderation 260 return '0'; 261 262 case '3': 263 // Mark as spam 264 return 'spam'; 265 266 case '4': 267 // Send to trash 268 return 'trash'; 269 270 default: 271 // Default to moderation 272 return $approved; 273 } 274 } 297 wp_cache_set($cache_key, $analysis, '', 60); 298 } 299 300 // Use analysis result (either cached or fresh) to determine approval 301 if ($analysis) { 302 return $this->get_approval_status($analysis, $approved); 303 } 304 305 // Fallback if no analysis available 306 return $approved; 275 307 276 308 … … 287 319 288 320 public function moderation_api_comment_post( $comment_ID, $comment_approved ) { 321 // Get the comment object and reconstruct the cache key 322 $comment = get_comment($comment_ID); 323 $cache_key = 'modapi_analysis_' . md5($comment->comment_content . $comment->comment_author_IP); 324 289 325 // Get the cached analysis results 290 $analysis = wp_cache_get( 'modapi_analysis_' . get_comment($comment_ID)->comment_author_IP);326 $analysis = wp_cache_get($cache_key); 291 327 292 328 if ($analysis) { 293 329 // Store the analysis results as comment meta 294 330 add_comment_meta($comment_ID, 'modapi_flagged', $analysis['flagged']); 295 331 if (isset($analysis['contentId'])) { 332 add_comment_meta($comment_ID, 'modapi_content_id', $analysis['contentId']); 333 } 296 334 // Clear the cache 297 wp_cache_delete('modapi_analysis_' . get_comment($comment_ID)->comment_author_IP); 335 wp_cache_delete($cache_key); 336 } 337 } 338 339 340 /** 341 * Find comment by content ID across multisite network 342 * 343 * @param string $content_id The content ID to search for 344 * @return array|false Array with comment_id and site_id if found, false otherwise 345 */ 346 private function find_comment_by_content_id($content_id) { 347 if (is_multisite()) { 348 // Search across all sites in the network 349 $sites = get_sites(); 350 foreach ($sites as $site) { 351 switch_to_blog($site->blog_id); 352 353 $comments = get_comments(array( 354 'meta_key' => 'modapi_content_id', 355 'meta_value' => $content_id, 356 'number' => 1 357 )); 358 359 if (!empty($comments)) { 360 $result = array( 361 'comment_id' => $comments[0]->comment_ID, 362 'site_id' => $site->blog_id 363 ); 364 restore_current_blog(); 365 return $result; 366 } 367 368 restore_current_blog(); 369 } 370 return false; 371 } else { 372 // Single site - search current site only 373 $comments = get_comments(array( 374 'meta_key' => 'modapi_content_id', 375 'meta_value' => $content_id, 376 'number' => 1 377 )); 378 379 if (!empty($comments)) { 380 return array( 381 'comment_id' => $comments[0]->comment_ID, 382 'site_id' => null 383 ); 384 } 385 386 return false; 387 } 388 } 389 390 /** 391 * Handle comment edits by re-analyzing with existing content ID 392 * 393 * @param int $comment_ID The comment ID being edited 394 * @param array $data The comment data being updated 395 */ 396 public function edit_comment_handler($comment_ID, $data) { 397 // Get the existing comment 398 $comment = get_comment($comment_ID); 399 if (!$comment) { 400 return; 401 } 402 403 // Get the existing content ID to maintain history 404 $existing_content_id = get_comment_meta($comment_ID, 'modapi_content_id', true); 405 if (!$existing_content_id) { 406 // No existing content ID, skip analysis 407 return; 408 } 409 410 // Create a comment object with the new data for analysis 411 $updated_comment = (object) array_merge((array) $comment, $data); 412 413 // Analyze the updated comment with the existing content ID 414 $analysis = $this->analyze_comment($updated_comment, $existing_content_id); 415 416 if ($analysis !== false) { 417 // Update the comment meta with new analysis results 418 update_comment_meta($comment_ID, 'modapi_flagged', $analysis['flagged']); 419 420 // Update content ID if it changed (though it shouldn't for edits) 421 if (isset($analysis['contentId'])) { 422 update_comment_meta($comment_ID, 'modapi_content_id', $analysis['contentId']); 423 } 424 425 // Apply moderation action based on new analysis 426 $this->apply_moderation_action($comment_ID, $analysis); 427 } 428 } 429 430 /** 431 * Apply moderation action to a comment based on analysis results 432 * 433 * @param int $comment_ID The comment ID 434 * @param array $analysis The analysis results 435 */ 436 private function apply_moderation_action($comment_ID, $analysis) { 437 if (!$analysis['flagged']) { 438 // If not flagged, approve the comment 439 wp_set_comment_status($comment_ID, 'approve'); 440 return; 441 } 442 443 $action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action', '2'); 444 445 switch ($action) { 446 case '1': 447 // Let comment through (approve) 448 wp_set_comment_status($comment_ID, 'approve'); 449 break; 450 451 case '2': 452 // Hold for moderation 453 wp_set_comment_status($comment_ID, 'hold'); 454 break; 455 456 case '3': 457 // Mark as spam 458 wp_spam_comment($comment_ID); 459 break; 460 461 case '4': 462 // Send to trash 463 wp_trash_comment($comment_ID); 464 break; 465 466 default: 467 // Default to hold for moderation 468 wp_set_comment_status($comment_ID, 'hold'); 469 break; 470 } 471 } 472 473 /** 474 * Get the approval status based on analysis results 475 * 476 * @param array $analysis Analysis results from moderation API 477 * @param mixed $default_approved Default approval status 478 * @return mixed Approval status 479 */ 480 private function get_approval_status($analysis, $default_approved) { 481 if (!$analysis['flagged']) { 482 return $default_approved; 483 } 484 485 $action = is_multisite() ? Moderation_Api_Network_Admin::get_effective_flagged_action() : get_option('modapi_flagged_action', '2'); 486 487 switch ($action) { 488 case '1': 489 // Let comment through 490 return '1'; 491 492 case '2': 493 // Hold for moderation 494 return '0'; 495 496 case '3': 497 // Mark as spam 498 return 'spam'; 499 500 case '4': 501 // Send to trash 502 return 'trash'; 503 504 default: 505 // Default to original approval status 506 return $default_approved; 298 507 } 299 508 } -
moderation-api-automated-content-moderation/trunk/release-plugin.sh
r3143763 r3326772 5 5 SVN_REPO=$(realpath "../moderation-api-automated-content-moderation") 6 6 PLUGIN_SLUG="moderation-api-automated-content-moderation" 7 SVN_USERNAME="moderationapi" 7 8 8 9 # Ensure we're in the Git repository … … 34 35 # Update SVN repository 35 36 cd "$SVN_REPO" || exit 1 36 svn update 37 svn update --username "$SVN_USERNAME" 37 38 38 39 # Remove existing files in trunk … … 50 51 51 52 # Add new files to SVN 52 svn add trunk/* assets/* --force 53 svn add trunk/* assets/* --force --username "$SVN_USERNAME" 53 54 54 55 # Remove deleted files from SVN 55 svn status | grep '^\!' | sed 's/! *//' | xargs -I% svn rm %@ 56 svn status | grep '^\!' | sed 's/! *//' | xargs -I% svn rm %@ --username "$SVN_USERNAME" 56 57 57 58 # Commit changes to trunk 58 svn commit -m "Update to version $VERSION" 59 svn commit -m "Update to version $VERSION" --username "$SVN_USERNAME" 59 60 60 61 # Create new tag 61 svn copy trunk "tags/$VERSION" 62 svn commit -m "Tagging version $VERSION" 62 svn copy trunk "tags/$VERSION" --username "$SVN_USERNAME" 63 svn commit -m "Tagging version $VERSION" --username "$SVN_USERNAME" 63 64 64 65 echo "Plugin $PLUGIN_SLUG version $VERSION has been successfully released!"
Note: See TracChangeset
for help on using the changeset viewer.