Plugin Directory

Changeset 3326772


Ignore:
Timestamp:
07/12/2025 01:33:21 PM (7 months ago)
Author:
moderationapi
Message:

Update to version 1.0.3

Location:
moderation-api-automated-content-moderation
Files:
27 added
3 deleted
25 edited
2 copied

Legend:

Unmodified
Added
Removed
  • moderation-api-automated-content-moderation/tags/1.0.3/admin/class-moderation-api-admin.php

    r3161458 r3326772  
    2121 * @author     Moderation API <[email protected]>
    2222 */
    23 class Moderation_Api_Admin {
     23class Moderation_Api_Admin
     24{
    2425
    2526    const NONCE = 'modapi-update-key';
     
    5152     * @param      string    $version    The version of this plugin.
    5253     */
    53     public function __construct( $plugin_name, $version ) {
     54    public function __construct($plugin_name, $version)
     55    {
    5456
    5557        $this->plugin_name = $plugin_name;
     
    5759
    5860    }
    59    
    60    
     61
     62
    6163    /**
    6264     * Register the stylesheets for the admin area.
     
    6466     * @since    1.0.0
    6567     */
    66     public function enqueue_styles() {
     68    public function enqueue_styles()
     69    {
    6770
    6871        /**
     
    7881         */
    7982
    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');
    8184
    8285    }
     
    8790     * @since    1.0.0
    8891     */
    89     public function enqueue_scripts() {
     92    public function enqueue_scripts()
     93    {
    9094
    9195        /**
     
    101105         */
    102106
    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    {
    107112        // 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    {
    114120        // check page is moderation-api
    115121        if (!isset($_GET['page']) || $_GET['page'] !== 'moderation-api') {
     
    133139    }
    134140
    135     private function handle_post_request(){
     141    private function handle_post_request()
     142    {
    136143        $this->check_nonce();
    137144        $action = $this->get_action();
     
    139146    }
    140147
    141     private function handle_get_request(){
     148    private function handle_get_request()
     149    {
    142150        $action = $this->get_action();
    143151        $this->handle_action($action);
    144152    }
    145153
    146     private function get_action(){
     154    private function get_action()
     155    {
    147156        // get action from post or get
    148157        $action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : null;
     
    150159            $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
    151160        }
    152         if(!$action && isset($_GET['token'])){
     161        if (!$action && isset($_GET['token'])) {
    153162            $action = 'enter-key';
    154163        }
     
    157166
    158167
    159     private function handle_action($action){
     168    private function handle_action($action)
     169    {
    160170        if ($action === 'enter-key') {
    161171            $api_key = sanitize_text_field($_POST['key'] ?? $_GET['token']);
     
    165175            $this->disconnect_key();
    166176        }
    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    {
    171194        if (!$api_key) {
    172195            return;
     
    176199
    177200            // invalid key show notice
    178             add_action('admin_notices', function() {
     201            add_action('admin_notices', function () {
    179202                ?>
    180203                <div class="notice notice-error is-dismissible">
     
    186209        }
    187210
    188     foreach (array('modapi_flagged_action') as $option) {
    189       // Sanitize the input
    190       $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3';
    191    
    192       // Escape the output before saving it to the database
    193       $escaped_value = esc_attr($input_value);
    194    
    195       // Update the option in the database
    196       update_option($option, $escaped_value);
    197     }
    198 
    199211        $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    {
    211277        // remove the api key
    212278        update_option('moderation_api_key', '');
    213279    }
    214280
    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    {
    216317        // include key as header bearer
    217318        $headers = array(
     
    220321        );
    221322        $url = Moderation_Api::API_URL . "/api/v1/account";
    222        
     323
    223324
    224325        $response = wp_remote_get($url, array(
     
    237338
    238339
    239     function validate_api_key($key) {
     340    function validate_api_key($key)
     341    {
    240342
    241343        if (!$key) {
     
    243345        }
    244346        try {
    245        
     347
    246348            $account = $this->get_account($key);
    247349            if ($account && $account->id) {
     
    258360
    259361
    260     public function add_admin_menu() {
     362    public function add_admin_menu()
     363    {
    261364        add_options_page('Moderation API', 'Moderation API', 'manage_options', 'moderation-api', array($this, 'display_page'));
    262365    }
    263366
    264     public function display_page() {
     367    public function display_page()
     368    {
    265369        $api_key = Moderation_Api::get_api_key();
    266370
    267371        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();
    275375                return;
     376            }
     377
     378            Moderation_Api::view('config', array('modapi_user' => $account));
     379            return;
    276380        }
    277381
     
    284388
    285389
    286     public static function get_page_url($page = 'config') {
     390    public static function get_page_url($page = 'config')
     391    {
    287392
    288393        $args = array('page' => 'moderation-api');
     
    298403        return add_query_arg($args, menu_page_url('moderation-api', false));
    299404    }
    300    
     405
    301406
    302407    // Add a custom column to the comments list table
    303     function custom_comments_column($columns) {
     408    function custom_comments_column($columns)
     409    {
    304410        // Add a new column
    305411        $columns['custom_flagged'] = 'Flagged';
     
    308414
    309415    // 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    {
    311418        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>';
    336424            }
    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    {
    341451
    342452        $apiKey = Moderation_Api::get_api_key();
     
    355465                        )
    356466                    )
    357                         ),
     467                ),
    358468                array(
    359469                    'name' => 'Show on WP',
  • moderation-api-automated-content-moderation/tags/1.0.3/admin/css/moderation-api-admin.css

    r3177384 r3326772  
    466466}
    467467
    468 .modapi-card .modapi-card-actions {
    469   margin-top: 1rem;
    470 }
    471 
    472468.jetpack_page_modapi-key-config .update-nag,
    473469.settings_page_moderation-api .update-nag {
     
    537533.modapi-box-header {
    538534  max-width: 700px;
    539   margin: 0 auto 40px auto;
     535  margin: 0 auto 0px auto;
    540536  line-height: 1.5;
    541537}
     
    627623}
    628624
    629 .modapi-button.secondary {
     625#modapi-plugin-container .modapi-button.secondary {
    630626  background: #f5f5f5;
    631627  border-color: #c8d7e1;
    632628  color: #2e4453;
    633629}
    634 .modapi-button.secondary:hover {
     630#modapi-plugin-container .modapi-button.secondary:hover {
    635631  background: #e6ecf1;
    636632  border-color: #a8bece;
     
    886882}
    887883
     884.modapi-settings__row-input .description {
     885  margin-top: 0.5em;
     886}
     887
    888888.modapi-settings__row-title {
    889889  font-weight: 500;
     
    891891  margin: 0;
    892892  margin-bottom: 1em;
     893  align-items: center;
    893894}
    894895
     
    899900.modapi-card-actions {
    900901  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;
    901913}
    902914
    903915.modapi-settings__row label {
    904916  padding-bottom: 1em;
     917}
     918.modapi-settings__row-title label {
     919  padding-bottom: 0 !important;
    905920}
    906921
  • moderation-api-automated-content-moderation/tags/1.0.3/admin/views/config.php

    r3143763 r3326772  
    11<?php
    2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     2if (!defined('ABSPATH'))
     3    exit; // Exit if accessed directly
    34
    45//phpcs:disable VariableAnalysis
     
    910    <div class="modapi-masthead">
    1011        <div class="modapi-masthead__inside-container">
    11             <?php Moderation_Api::view( 'logo' ); ?>
     12            <?php Moderation_Api::view('logo'); ?>
    1213        </div>
    1314    </div>
    1415    <div class="modapi-lower">
    15         <?php if ( Moderation_Api::get_api_key() ) { ?>
     16        <?php if (Moderation_Api::get_api_key()) { ?>
    1617            <?php Moderation_Api::display_status(); ?>
    1718        <?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); ?>
    2122            <?php } ?>
    2223        <?php } ?>
    2324
    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>
    3174                    </div>
    3275
    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>
    8382                    </div>
    8483                </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            ?>
    8694            <div class="modapi-card">
    8795                <div class="modapi-section-header">
    8896                    <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>
    9098                    </h2>
    9199                </div>
    92100
    93101                <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
    96198                        <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">
    98354                                    <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'); ?>
    100356                                    </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; ?>
    126386                                </div>
    127387                                <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
    130402                                        <div>
    131403                                            <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; ?> />
    133406                                                <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'); ?>
    135408                                                </span>
    136409                                            </label>
     
    138411                                        <div>
    139412                                            <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; ?> />
    141415                                                <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'); ?>
    143417                                                </span>
    144418                                            </label>
     
    147421                                        <div>
    148422                                            <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; ?> />
    150425                                                <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'); ?>
    152427                                                </span>
    153428                                            </label>
    154429                                        </div>
    155                                        
     430
    156431                                        <div>
    157432                                            <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; ?> />
    159435                                                <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'); ?>
    161437                                                </span>
    162438                                            </label>
     
    164440                                        <div>
    165441                                            <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; ?> />
    167444                                                <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'); ?>
    169446                                                </span>
    170447                                            </label>
     
    174451
    175452                                    <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.
    178457                                    </div>
    179                                    
     458
    180459                                </div>
    181460                            </div>
    182                                
    183                            
    184461                        </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>
    196497                        </div>
    197                     </form>
     498                    <?php endif; ?>
     499
     500
    198501                </div>
    199502            </div>
    200503
    201            
     504        <?php endif; // End if site is not disabled ?>
     505
    202506    </div>
    203507</div>
  • moderation-api-automated-content-moderation/tags/1.0.3/development.md

    r3177384 r3326772  
    1313npx @wordpress/env start
    1414```
     15
     16## Admin credentials
     17
     18admin
     19password
  • moderation-api-automated-content-moderation/tags/1.0.3/includes/class-moderation-api-activator.php

    r3143763 r3326772  
    3131     */
    3232    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    }
    3338
     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
    3457    }
    3558
  • moderation-api-automated-content-moderation/tags/1.0.3/includes/class-moderation-api.php

    r3177384 r3326772  
    2828 * @author     Moderation API <[email protected]>
    2929 */
    30 class Moderation_Api {
     30class Moderation_Api
     31{
    3132
    3233    /**
     
    7071     * @since    1.0.0
    7172     */
    72     public function __construct() {
    73         if ( defined( 'MODERATION_API_VERSION' ) ) {
     73    public function __construct()
     74    {
     75        if (defined('MODERATION_API_VERSION')) {
    7476            $this->version = MODERATION_API_VERSION;
    7577        } else {
     
    8385        $this->define_public_hooks();
    8486
     87        if (is_multisite()) {
     88            $this->define_network_admin_hooks();
     89        }
     90
    8591    }
    8692
     
    101107     * @access   private
    102108     */
    103     private function load_dependencies() {
     109    private function load_dependencies()
     110    {
    104111
    105112        /**
     
    107114         * core plugin.
    108115         */
    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';
    110117
    111118        /**
     
    113120         * of the plugin.
    114121         */
    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';
    116123
    117124        /**
    118125         * The class responsible for defining all actions that occur in the admin area.
    119126         */
    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';
    121128
    122129        /**
     
    124131         * side of the site.
    125132         */
    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        }
    127141
    128142        $this->loader = new Moderation_Api_Loader();
     
    139153     * @access   private
    140154     */
    141     private function set_locale() {
     155    private function set_locale()
     156    {
    142157
    143158        $plugin_i18n = new Moderation_Api_i18n();
    144159
    145         $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );
     160        $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');
    146161
    147162    }
     
    154169     * @access   private
    155170     */
    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');
    162178
    163179        // 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');
    165181
    166182        // 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);
    172188
    173189
     
    177193        // Add a custom column to the comments list table
    178194        $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');
    181197
    182198    }
     
    191207     * @access   private
    192208     */
    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');
    199216
    200217        $this->loader->add_action('comment_post', $plugin_public, 'moderation_api_comment_post', 10, 2);
     
    207224    }
    208225
    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
    212252
    213253    /**
     
    216256     * @since    1.0.0
    217257     */
    218     public function run() {
     258    public function run()
     259    {
    219260        $this->loader->run();
    220261    }
     
    227268     * @return    string    The name of the plugin.
    228269     */
    229     public function get_plugin_name() {
     270    public function get_plugin_name()
     271    {
    230272        return $this->plugin_name;
    231273    }
     
    237279     * @return    Moderation_Api_Loader    Orchestrates the hooks of the plugin.
    238280     */
    239     public function get_loader() {
     281    public function get_loader()
     282    {
    240283        return $this->loader;
    241284    }
     
    247290     * @return    string    The version number of the plugin.
    248291     */
    249     public function get_version() {
     292    public function get_version()
     293    {
    250294        return $this->version;
    251295    }
    252296
    253297
    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) {
    258303            $$key = $val;
    259304        }
    260        
     305
    261306        // load_plugin_textdomain( 'modapi' );
    262307
    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');
    280343    }
    281344
     
    283346     * Display admin notices on the Comments page.
    284347     */
    285     public function add_admin_notices() {
     348    public function add_admin_notices()
     349    {
    286350        // Get the current screen
    287351        $screen = get_current_screen();
    288352
    289353        // Check if we are on the Comments admin page
    290         if ( 'edit-comments' === $screen->id ) {
     354        if ('edit-comments' === $screen->id) {
    291355            $api_key = Moderation_Api::get_api_key();
    292356
    293357            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                ));
    298362            }
    299363        }
  • moderation-api-automated-content-moderation/tags/1.0.3/moderation-api.php

    r3177384 r3326772  
    1717 * Plugin URI:        https://moderationapi.com/integrations/wordpress-content-moderation
    1818 * 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.2
     19 * Version:           1.0.3
    2020 * Author:            Moderation API
    2121 * Author URI:        https://moderationapi.com/
     
    3636 * Rename this for your plugin and update it as you release new versions.
    3737 */
    38 define( 'MODERATION_API_VERSION', '1.0.2' );
     38define( 'MODERATION_API_VERSION', '1.0.3' );
    3939
    4040define( 'MODERATION_API_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     
    6262
    6363/**
     64 * The code that runs during plugin network activation for new sites.
     65 */
     66function 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
     71if (is_multisite()) {
     72    add_action('wpmu_new_blog', 'moderation_api_network_activate');
     73}
     74
     75/**
    6476 * The core plugin class that is used to define internationalization,
    6577 * 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  
    2424
    2525    /**
     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    /**
    2635     * The ID of this plugin.
    2736     *
     
    5362        $this->version = $version;
    5463
    55         // Add the pre-approval filter with priority 20 (after default filters)
    5664        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);
    5866    }
    5967
     
    105113
    106114
    107     public function analyze_comment($comment){
     115    public function analyze_comment($comment, $content_id = null){
    108116        $api_key = Moderation_Api::get_api_key();
    109117
     
    113121        $user_email = sanitize_email($comment->comment_author_email);
    114122        $user_ip = sanitize_text_field($comment->comment_author_IP);
    115         // $comment_id = absint($comment->comment_ID);
    116123        $post_id = absint($comment->comment_post_ID);
    117124        $post_url = esc_url(get_permalink($post_id));
     
    124131            $authorId = $user_ip;
    125132        }
     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;
    126137
    127138        $url = Moderation_Api::API_URL . "/api/v1/moderate/text";
     
    135146            'contextId' => (string) $post_id,
    136147            'metadata' => array(
    137                 // 'comment_id' => $comment_id,
    138                 'url' => $post_url
     148                'url' => $post_url . '#comment-' . $predicted_comment_id
    139149            )
    140150        );
     151       
     152        // If content_id is provided (for edits), include it to maintain history
     153        if ($content_id) {
     154            $data['contentId'] = $content_id;
     155        }
    141156
    142157        $response = wp_remote_post($url, array(
     
    149164            // Decode the JSON response
    150165            $responseData = json_decode($body, true);
     166           
     167            // Keep contentId from API response as-is
     168           
    151169            // Return the response data
    152170            return $responseData;
     
    179197        $json = json_decode($raw_data, true);
    180198        $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           
    185213            // Sanitize and validate the action
    186214            $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : '';
     
    191219            }
    192220
    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);
    198224            }
    199225                   
     
    213239            }
    214240
     241            // Restore original blog if we switched
     242            if (is_multisite() && $site_id) {
     243                restore_current_blog();
     244            }
     245
    215246            wp_send_json_success($action);
    216247            exit();
     
    233264    public function pre_comment_approved_filter($approved, $commentdata) {
    234265        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
    235272            // Convert commentdata array to object for consistency
    236273            $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) {
    242293                    // On API failure, let other filters handle it
    243294                    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                }
    252296               
    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;
    275307
    276308           
     
    287319
    288320    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       
    289325        // 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);
    291327       
    292328        if ($analysis) {
    293329            // Store the analysis results as comment meta
    294330            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            }
    296334            // 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;
    298507        }
    299508    }
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/admin/class-moderation-api-admin.php

    r3161458 r3326772  
    2121 * @author     Moderation API <[email protected]>
    2222 */
    23 class Moderation_Api_Admin {
     23class Moderation_Api_Admin
     24{
    2425
    2526    const NONCE = 'modapi-update-key';
     
    5152     * @param      string    $version    The version of this plugin.
    5253     */
    53     public function __construct( $plugin_name, $version ) {
     54    public function __construct($plugin_name, $version)
     55    {
    5456
    5557        $this->plugin_name = $plugin_name;
     
    5759
    5860    }
    59    
    60    
     61
     62
    6163    /**
    6264     * Register the stylesheets for the admin area.
     
    6466     * @since    1.0.0
    6567     */
    66     public function enqueue_styles() {
     68    public function enqueue_styles()
     69    {
    6770
    6871        /**
     
    7881         */
    7982
    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');
    8184
    8285    }
     
    8790     * @since    1.0.0
    8891     */
    89     public function enqueue_scripts() {
     92    public function enqueue_scripts()
     93    {
    9094
    9195        /**
     
    101105         */
    102106
    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    {
    107112        // 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    {
    114120        // check page is moderation-api
    115121        if (!isset($_GET['page']) || $_GET['page'] !== 'moderation-api') {
     
    133139    }
    134140
    135     private function handle_post_request(){
     141    private function handle_post_request()
     142    {
    136143        $this->check_nonce();
    137144        $action = $this->get_action();
     
    139146    }
    140147
    141     private function handle_get_request(){
     148    private function handle_get_request()
     149    {
    142150        $action = $this->get_action();
    143151        $this->handle_action($action);
    144152    }
    145153
    146     private function get_action(){
     154    private function get_action()
     155    {
    147156        // get action from post or get
    148157        $action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : null;
     
    150159            $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
    151160        }
    152         if(!$action && isset($_GET['token'])){
     161        if (!$action && isset($_GET['token'])) {
    153162            $action = 'enter-key';
    154163        }
     
    157166
    158167
    159     private function handle_action($action){
     168    private function handle_action($action)
     169    {
    160170        if ($action === 'enter-key') {
    161171            $api_key = sanitize_text_field($_POST['key'] ?? $_GET['token']);
     
    165175            $this->disconnect_key();
    166176        }
    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    {
    171194        if (!$api_key) {
    172195            return;
     
    176199
    177200            // invalid key show notice
    178             add_action('admin_notices', function() {
     201            add_action('admin_notices', function () {
    179202                ?>
    180203                <div class="notice notice-error is-dismissible">
     
    186209        }
    187210
    188     foreach (array('modapi_flagged_action') as $option) {
    189       // Sanitize the input
    190       $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3';
    191    
    192       // Escape the output before saving it to the database
    193       $escaped_value = esc_attr($input_value);
    194    
    195       // Update the option in the database
    196       update_option($option, $escaped_value);
    197     }
    198 
    199211        $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    {
    211277        // remove the api key
    212278        update_option('moderation_api_key', '');
    213279    }
    214280
    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    {
    216317        // include key as header bearer
    217318        $headers = array(
     
    220321        );
    221322        $url = Moderation_Api::API_URL . "/api/v1/account";
    222        
     323
    223324
    224325        $response = wp_remote_get($url, array(
     
    237338
    238339
    239     function validate_api_key($key) {
     340    function validate_api_key($key)
     341    {
    240342
    241343        if (!$key) {
     
    243345        }
    244346        try {
    245        
     347
    246348            $account = $this->get_account($key);
    247349            if ($account && $account->id) {
     
    258360
    259361
    260     public function add_admin_menu() {
     362    public function add_admin_menu()
     363    {
    261364        add_options_page('Moderation API', 'Moderation API', 'manage_options', 'moderation-api', array($this, 'display_page'));
    262365    }
    263366
    264     public function display_page() {
     367    public function display_page()
     368    {
    265369        $api_key = Moderation_Api::get_api_key();
    266370
    267371        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();
    275375                return;
     376            }
     377
     378            Moderation_Api::view('config', array('modapi_user' => $account));
     379            return;
    276380        }
    277381
     
    284388
    285389
    286     public static function get_page_url($page = 'config') {
     390    public static function get_page_url($page = 'config')
     391    {
    287392
    288393        $args = array('page' => 'moderation-api');
     
    298403        return add_query_arg($args, menu_page_url('moderation-api', false));
    299404    }
    300    
     405
    301406
    302407    // Add a custom column to the comments list table
    303     function custom_comments_column($columns) {
     408    function custom_comments_column($columns)
     409    {
    304410        // Add a new column
    305411        $columns['custom_flagged'] = 'Flagged';
     
    308414
    309415    // 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    {
    311418        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>';
    336424            }
    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    {
    341451
    342452        $apiKey = Moderation_Api::get_api_key();
     
    355465                        )
    356466                    )
    357                         ),
     467                ),
    358468                array(
    359469                    'name' => 'Show on WP',
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/admin/css/moderation-api-admin.css

    r3177384 r3326772  
    466466}
    467467
    468 .modapi-card .modapi-card-actions {
    469   margin-top: 1rem;
    470 }
    471 
    472468.jetpack_page_modapi-key-config .update-nag,
    473469.settings_page_moderation-api .update-nag {
     
    537533.modapi-box-header {
    538534  max-width: 700px;
    539   margin: 0 auto 40px auto;
     535  margin: 0 auto 0px auto;
    540536  line-height: 1.5;
    541537}
     
    627623}
    628624
    629 .modapi-button.secondary {
     625#modapi-plugin-container .modapi-button.secondary {
    630626  background: #f5f5f5;
    631627  border-color: #c8d7e1;
    632628  color: #2e4453;
    633629}
    634 .modapi-button.secondary:hover {
     630#modapi-plugin-container .modapi-button.secondary:hover {
    635631  background: #e6ecf1;
    636632  border-color: #a8bece;
     
    886882}
    887883
     884.modapi-settings__row-input .description {
     885  margin-top: 0.5em;
     886}
     887
    888888.modapi-settings__row-title {
    889889  font-weight: 500;
     
    891891  margin: 0;
    892892  margin-bottom: 1em;
     893  align-items: center;
    893894}
    894895
     
    899900.modapi-card-actions {
    900901  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;
    901913}
    902914
    903915.modapi-settings__row label {
    904916  padding-bottom: 1em;
     917}
     918.modapi-settings__row-title label {
     919  padding-bottom: 0 !important;
    905920}
    906921
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/admin/views/config.php

    r3143763 r3326772  
    11<?php
    2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     2if (!defined('ABSPATH'))
     3    exit; // Exit if accessed directly
    34
    45//phpcs:disable VariableAnalysis
     
    910    <div class="modapi-masthead">
    1011        <div class="modapi-masthead__inside-container">
    11             <?php Moderation_Api::view( 'logo' ); ?>
     12            <?php Moderation_Api::view('logo'); ?>
    1213        </div>
    1314    </div>
    1415    <div class="modapi-lower">
    15         <?php if ( Moderation_Api::get_api_key() ) { ?>
     16        <?php if (Moderation_Api::get_api_key()) { ?>
    1617            <?php Moderation_Api::display_status(); ?>
    1718        <?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); ?>
    2122            <?php } ?>
    2223        <?php } ?>
    2324
    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>
    3174                    </div>
    3275
    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>
    8382                    </div>
    8483                </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            ?>
    8694            <div class="modapi-card">
    8795                <div class="modapi-section-header">
    8896                    <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>
    9098                    </h2>
    9199                </div>
    92100
    93101                <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
    96198                        <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">
    98354                                    <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'); ?>
    100356                                    </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; ?>
    126386                                </div>
    127387                                <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
    130402                                        <div>
    131403                                            <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; ?> />
    133406                                                <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'); ?>
    135408                                                </span>
    136409                                            </label>
     
    138411                                        <div>
    139412                                            <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; ?> />
    141415                                                <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'); ?>
    143417                                                </span>
    144418                                            </label>
     
    147421                                        <div>
    148422                                            <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; ?> />
    150425                                                <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'); ?>
    152427                                                </span>
    153428                                            </label>
    154429                                        </div>
    155                                        
     430
    156431                                        <div>
    157432                                            <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; ?> />
    159435                                                <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'); ?>
    161437                                                </span>
    162438                                            </label>
     
    164440                                        <div>
    165441                                            <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; ?> />
    167444                                                <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'); ?>
    169446                                                </span>
    170447                                            </label>
     
    174451
    175452                                    <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.
    178457                                    </div>
    179                                    
     458
    180459                                </div>
    181460                            </div>
    182                                
    183                            
    184461                        </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>
    196497                        </div>
    197                     </form>
     498                    <?php endif; ?>
     499
     500
    198501                </div>
    199502            </div>
    200503
    201            
     504        <?php endif; // End if site is not disabled ?>
     505
    202506    </div>
    203507</div>
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/development.md

    r3177384 r3326772  
    1313npx @wordpress/env start
    1414```
     15
     16## Admin credentials
     17
     18admin
     19password
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/includes/class-moderation-api-activator.php

    r3143763 r3326772  
    3131     */
    3232    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    }
    3338
     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
    3457    }
    3558
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/includes/class-moderation-api.php

    r3177384 r3326772  
    2828 * @author     Moderation API <[email protected]>
    2929 */
    30 class Moderation_Api {
     30class Moderation_Api
     31{
    3132
    3233    /**
     
    7071     * @since    1.0.0
    7172     */
    72     public function __construct() {
    73         if ( defined( 'MODERATION_API_VERSION' ) ) {
     73    public function __construct()
     74    {
     75        if (defined('MODERATION_API_VERSION')) {
    7476            $this->version = MODERATION_API_VERSION;
    7577        } else {
     
    8385        $this->define_public_hooks();
    8486
     87        if (is_multisite()) {
     88            $this->define_network_admin_hooks();
     89        }
     90
    8591    }
    8692
     
    101107     * @access   private
    102108     */
    103     private function load_dependencies() {
     109    private function load_dependencies()
     110    {
    104111
    105112        /**
     
    107114         * core plugin.
    108115         */
    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';
    110117
    111118        /**
     
    113120         * of the plugin.
    114121         */
    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';
    116123
    117124        /**
    118125         * The class responsible for defining all actions that occur in the admin area.
    119126         */
    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';
    121128
    122129        /**
     
    124131         * side of the site.
    125132         */
    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        }
    127141
    128142        $this->loader = new Moderation_Api_Loader();
     
    139153     * @access   private
    140154     */
    141     private function set_locale() {
     155    private function set_locale()
     156    {
    142157
    143158        $plugin_i18n = new Moderation_Api_i18n();
    144159
    145         $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );
     160        $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');
    146161
    147162    }
     
    154169     * @access   private
    155170     */
    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');
    162178
    163179        // 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');
    165181
    166182        // 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);
    172188
    173189
     
    177193        // Add a custom column to the comments list table
    178194        $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');
    181197
    182198    }
     
    191207     * @access   private
    192208     */
    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');
    199216
    200217        $this->loader->add_action('comment_post', $plugin_public, 'moderation_api_comment_post', 10, 2);
     
    207224    }
    208225
    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
    212252
    213253    /**
     
    216256     * @since    1.0.0
    217257     */
    218     public function run() {
     258    public function run()
     259    {
    219260        $this->loader->run();
    220261    }
     
    227268     * @return    string    The name of the plugin.
    228269     */
    229     public function get_plugin_name() {
     270    public function get_plugin_name()
     271    {
    230272        return $this->plugin_name;
    231273    }
     
    237279     * @return    Moderation_Api_Loader    Orchestrates the hooks of the plugin.
    238280     */
    239     public function get_loader() {
     281    public function get_loader()
     282    {
    240283        return $this->loader;
    241284    }
     
    247290     * @return    string    The version number of the plugin.
    248291     */
    249     public function get_version() {
     292    public function get_version()
     293    {
    250294        return $this->version;
    251295    }
    252296
    253297
    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) {
    258303            $$key = $val;
    259304        }
    260        
     305
    261306        // load_plugin_textdomain( 'modapi' );
    262307
    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');
    280343    }
    281344
     
    283346     * Display admin notices on the Comments page.
    284347     */
    285     public function add_admin_notices() {
     348    public function add_admin_notices()
     349    {
    286350        // Get the current screen
    287351        $screen = get_current_screen();
    288352
    289353        // Check if we are on the Comments admin page
    290         if ( 'edit-comments' === $screen->id ) {
     354        if ('edit-comments' === $screen->id) {
    291355            $api_key = Moderation_Api::get_api_key();
    292356
    293357            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                ));
    298362            }
    299363        }
  • moderation-api-automated-content-moderation/tags/1.0.3/trunk/moderation-api.php

    r3177384 r3326772  
    1717 * Plugin URI:        https://moderationapi.com/integrations/wordpress-content-moderation
    1818 * 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.2
     19 * Version:           1.0.3
    2020 * Author:            Moderation API
    2121 * Author URI:        https://moderationapi.com/
     
    3636 * Rename this for your plugin and update it as you release new versions.
    3737 */
    38 define( 'MODERATION_API_VERSION', '1.0.2' );
     38define( 'MODERATION_API_VERSION', '1.0.3' );
    3939
    4040define( 'MODERATION_API_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     
    6262
    6363/**
     64 * The code that runs during plugin network activation for new sites.
     65 */
     66function 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
     71if (is_multisite()) {
     72    add_action('wpmu_new_blog', 'moderation_api_network_activate');
     73}
     74
     75/**
    6476 * The core plugin class that is used to define internationalization,
    6577 * 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  
    2424
    2525    /**
     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    /**
    2635     * The ID of this plugin.
    2736     *
     
    5362        $this->version = $version;
    5463
    55         // Add the pre-approval filter with priority 20 (after default filters)
    5664        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);
    5866    }
    5967
     
    105113
    106114
    107     public function analyze_comment($comment){
     115    public function analyze_comment($comment, $content_id = null){
    108116        $api_key = Moderation_Api::get_api_key();
    109117
     
    113121        $user_email = sanitize_email($comment->comment_author_email);
    114122        $user_ip = sanitize_text_field($comment->comment_author_IP);
    115         // $comment_id = absint($comment->comment_ID);
    116123        $post_id = absint($comment->comment_post_ID);
    117124        $post_url = esc_url(get_permalink($post_id));
     
    124131            $authorId = $user_ip;
    125132        }
     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;
    126137
    127138        $url = Moderation_Api::API_URL . "/api/v1/moderate/text";
     
    135146            'contextId' => (string) $post_id,
    136147            'metadata' => array(
    137                 // 'comment_id' => $comment_id,
    138                 'url' => $post_url
     148                'url' => $post_url . '#comment-' . $predicted_comment_id
    139149            )
    140150        );
     151       
     152        // If content_id is provided (for edits), include it to maintain history
     153        if ($content_id) {
     154            $data['contentId'] = $content_id;
     155        }
    141156
    142157        $response = wp_remote_post($url, array(
     
    149164            // Decode the JSON response
    150165            $responseData = json_decode($body, true);
     166           
     167            // Keep contentId from API response as-is
     168           
    151169            // Return the response data
    152170            return $responseData;
     
    179197        $json = json_decode($raw_data, true);
    180198        $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           
    185213            // Sanitize and validate the action
    186214            $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : '';
     
    191219            }
    192220
    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);
    198224            }
    199225                   
     
    213239            }
    214240
     241            // Restore original blog if we switched
     242            if (is_multisite() && $site_id) {
     243                restore_current_blog();
     244            }
     245
    215246            wp_send_json_success($action);
    216247            exit();
     
    233264    public function pre_comment_approved_filter($approved, $commentdata) {
    234265        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
    235272            // Convert commentdata array to object for consistency
    236273            $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) {
    242293                    // On API failure, let other filters handle it
    243294                    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                }
    252296               
    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;
    275307
    276308           
     
    287319
    288320    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       
    289325        // 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);
    291327       
    292328        if ($analysis) {
    293329            // Store the analysis results as comment meta
    294330            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            }
    296334            // 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;
    298507        }
    299508    }
  • moderation-api-automated-content-moderation/trunk/admin/class-moderation-api-admin.php

    r3161458 r3326772  
    2121 * @author     Moderation API <[email protected]>
    2222 */
    23 class Moderation_Api_Admin {
     23class Moderation_Api_Admin
     24{
    2425
    2526    const NONCE = 'modapi-update-key';
     
    5152     * @param      string    $version    The version of this plugin.
    5253     */
    53     public function __construct( $plugin_name, $version ) {
     54    public function __construct($plugin_name, $version)
     55    {
    5456
    5557        $this->plugin_name = $plugin_name;
     
    5759
    5860    }
    59    
    60    
     61
     62
    6163    /**
    6264     * Register the stylesheets for the admin area.
     
    6466     * @since    1.0.0
    6567     */
    66     public function enqueue_styles() {
     68    public function enqueue_styles()
     69    {
    6770
    6871        /**
     
    7881         */
    7982
    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');
    8184
    8285    }
     
    8790     * @since    1.0.0
    8891     */
    89     public function enqueue_scripts() {
     92    public function enqueue_scripts()
     93    {
    9094
    9195        /**
     
    101105         */
    102106
    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    {
    107112        // 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    {
    114120        // check page is moderation-api
    115121        if (!isset($_GET['page']) || $_GET['page'] !== 'moderation-api') {
     
    133139    }
    134140
    135     private function handle_post_request(){
     141    private function handle_post_request()
     142    {
    136143        $this->check_nonce();
    137144        $action = $this->get_action();
     
    139146    }
    140147
    141     private function handle_get_request(){
     148    private function handle_get_request()
     149    {
    142150        $action = $this->get_action();
    143151        $this->handle_action($action);
    144152    }
    145153
    146     private function get_action(){
     154    private function get_action()
     155    {
    147156        // get action from post or get
    148157        $action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : null;
     
    150159            $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
    151160        }
    152         if(!$action && isset($_GET['token'])){
     161        if (!$action && isset($_GET['token'])) {
    153162            $action = 'enter-key';
    154163        }
     
    157166
    158167
    159     private function handle_action($action){
     168    private function handle_action($action)
     169    {
    160170        if ($action === 'enter-key') {
    161171            $api_key = sanitize_text_field($_POST['key'] ?? $_GET['token']);
     
    165175            $this->disconnect_key();
    166176        }
    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    {
    171194        if (!$api_key) {
    172195            return;
     
    176199
    177200            // invalid key show notice
    178             add_action('admin_notices', function() {
     201            add_action('admin_notices', function () {
    179202                ?>
    180203                <div class="notice notice-error is-dismissible">
     
    186209        }
    187210
    188     foreach (array('modapi_flagged_action') as $option) {
    189       // Sanitize the input
    190       $input_value = isset($_POST[$option]) ? sanitize_text_field($_POST[$option]) : '3';
    191    
    192       // Escape the output before saving it to the database
    193       $escaped_value = esc_attr($input_value);
    194    
    195       // Update the option in the database
    196       update_option($option, $escaped_value);
    197     }
    198 
    199211        $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    {
    211277        // remove the api key
    212278        update_option('moderation_api_key', '');
    213279    }
    214280
    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    {
    216317        // include key as header bearer
    217318        $headers = array(
     
    220321        );
    221322        $url = Moderation_Api::API_URL . "/api/v1/account";
    222        
     323
    223324
    224325        $response = wp_remote_get($url, array(
     
    237338
    238339
    239     function validate_api_key($key) {
     340    function validate_api_key($key)
     341    {
    240342
    241343        if (!$key) {
     
    243345        }
    244346        try {
    245        
     347
    246348            $account = $this->get_account($key);
    247349            if ($account && $account->id) {
     
    258360
    259361
    260     public function add_admin_menu() {
     362    public function add_admin_menu()
     363    {
    261364        add_options_page('Moderation API', 'Moderation API', 'manage_options', 'moderation-api', array($this, 'display_page'));
    262365    }
    263366
    264     public function display_page() {
     367    public function display_page()
     368    {
    265369        $api_key = Moderation_Api::get_api_key();
    266370
    267371        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();
    275375                return;
     376            }
     377
     378            Moderation_Api::view('config', array('modapi_user' => $account));
     379            return;
    276380        }
    277381
     
    284388
    285389
    286     public static function get_page_url($page = 'config') {
     390    public static function get_page_url($page = 'config')
     391    {
    287392
    288393        $args = array('page' => 'moderation-api');
     
    298403        return add_query_arg($args, menu_page_url('moderation-api', false));
    299404    }
    300    
     405
    301406
    302407    // Add a custom column to the comments list table
    303     function custom_comments_column($columns) {
     408    function custom_comments_column($columns)
     409    {
    304410        // Add a new column
    305411        $columns['custom_flagged'] = 'Flagged';
     
    308414
    309415    // 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    {
    311418        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>';
    336424            }
    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    {
    341451
    342452        $apiKey = Moderation_Api::get_api_key();
     
    355465                        )
    356466                    )
    357                         ),
     467                ),
    358468                array(
    359469                    'name' => 'Show on WP',
  • moderation-api-automated-content-moderation/trunk/admin/css/moderation-api-admin.css

    r3177384 r3326772  
    466466}
    467467
    468 .modapi-card .modapi-card-actions {
    469   margin-top: 1rem;
    470 }
    471 
    472468.jetpack_page_modapi-key-config .update-nag,
    473469.settings_page_moderation-api .update-nag {
     
    537533.modapi-box-header {
    538534  max-width: 700px;
    539   margin: 0 auto 40px auto;
     535  margin: 0 auto 0px auto;
    540536  line-height: 1.5;
    541537}
     
    627623}
    628624
    629 .modapi-button.secondary {
     625#modapi-plugin-container .modapi-button.secondary {
    630626  background: #f5f5f5;
    631627  border-color: #c8d7e1;
    632628  color: #2e4453;
    633629}
    634 .modapi-button.secondary:hover {
     630#modapi-plugin-container .modapi-button.secondary:hover {
    635631  background: #e6ecf1;
    636632  border-color: #a8bece;
     
    886882}
    887883
     884.modapi-settings__row-input .description {
     885  margin-top: 0.5em;
     886}
     887
    888888.modapi-settings__row-title {
    889889  font-weight: 500;
     
    891891  margin: 0;
    892892  margin-bottom: 1em;
     893  align-items: center;
    893894}
    894895
     
    899900.modapi-card-actions {
    900901  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;
    901913}
    902914
    903915.modapi-settings__row label {
    904916  padding-bottom: 1em;
     917}
     918.modapi-settings__row-title label {
     919  padding-bottom: 0 !important;
    905920}
    906921
  • moderation-api-automated-content-moderation/trunk/admin/views/config.php

    r3143763 r3326772  
    11<?php
    2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     2if (!defined('ABSPATH'))
     3    exit; // Exit if accessed directly
    34
    45//phpcs:disable VariableAnalysis
     
    910    <div class="modapi-masthead">
    1011        <div class="modapi-masthead__inside-container">
    11             <?php Moderation_Api::view( 'logo' ); ?>
     12            <?php Moderation_Api::view('logo'); ?>
    1213        </div>
    1314    </div>
    1415    <div class="modapi-lower">
    15         <?php if ( Moderation_Api::get_api_key() ) { ?>
     16        <?php if (Moderation_Api::get_api_key()) { ?>
    1617            <?php Moderation_Api::display_status(); ?>
    1718        <?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); ?>
    2122            <?php } ?>
    2223        <?php } ?>
    2324
    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>
    3174                    </div>
    3275
    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>
    8382                    </div>
    8483                </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            ?>
    8694            <div class="modapi-card">
    8795                <div class="modapi-section-header">
    8896                    <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>
    9098                    </h2>
    9199                </div>
    92100
    93101                <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
    96198                        <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">
    98354                                    <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'); ?>
    100356                                    </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; ?>
    126386                                </div>
    127387                                <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
    130402                                        <div>
    131403                                            <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; ?> />
    133406                                                <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'); ?>
    135408                                                </span>
    136409                                            </label>
     
    138411                                        <div>
    139412                                            <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; ?> />
    141415                                                <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'); ?>
    143417                                                </span>
    144418                                            </label>
     
    147421                                        <div>
    148422                                            <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; ?> />
    150425                                                <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'); ?>
    152427                                                </span>
    153428                                            </label>
    154429                                        </div>
    155                                        
     430
    156431                                        <div>
    157432                                            <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; ?> />
    159435                                                <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'); ?>
    161437                                                </span>
    162438                                            </label>
     
    164440                                        <div>
    165441                                            <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; ?> />
    167444                                                <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'); ?>
    169446                                                </span>
    170447                                            </label>
     
    174451
    175452                                    <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.
    178457                                    </div>
    179                                    
     458
    180459                                </div>
    181460                            </div>
    182                                
    183                            
    184461                        </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>
    196497                        </div>
    197                     </form>
     498                    <?php endif; ?>
     499
     500
    198501                </div>
    199502            </div>
    200503
    201            
     504        <?php endif; // End if site is not disabled ?>
     505
    202506    </div>
    203507</div>
  • moderation-api-automated-content-moderation/trunk/development.md

    r3177384 r3326772  
    1313npx @wordpress/env start
    1414```
     15
     16## Admin credentials
     17
     18admin
     19password
  • moderation-api-automated-content-moderation/trunk/includes/class-moderation-api-activator.php

    r3143763 r3326772  
    3131     */
    3232    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    }
    3338
     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
    3457    }
    3558
  • moderation-api-automated-content-moderation/trunk/includes/class-moderation-api.php

    r3177384 r3326772  
    2828 * @author     Moderation API <[email protected]>
    2929 */
    30 class Moderation_Api {
     30class Moderation_Api
     31{
    3132
    3233    /**
     
    7071     * @since    1.0.0
    7172     */
    72     public function __construct() {
    73         if ( defined( 'MODERATION_API_VERSION' ) ) {
     73    public function __construct()
     74    {
     75        if (defined('MODERATION_API_VERSION')) {
    7476            $this->version = MODERATION_API_VERSION;
    7577        } else {
     
    8385        $this->define_public_hooks();
    8486
     87        if (is_multisite()) {
     88            $this->define_network_admin_hooks();
     89        }
     90
    8591    }
    8692
     
    101107     * @access   private
    102108     */
    103     private function load_dependencies() {
     109    private function load_dependencies()
     110    {
    104111
    105112        /**
     
    107114         * core plugin.
    108115         */
    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';
    110117
    111118        /**
     
    113120         * of the plugin.
    114121         */
    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';
    116123
    117124        /**
    118125         * The class responsible for defining all actions that occur in the admin area.
    119126         */
    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';
    121128
    122129        /**
     
    124131         * side of the site.
    125132         */
    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        }
    127141
    128142        $this->loader = new Moderation_Api_Loader();
     
    139153     * @access   private
    140154     */
    141     private function set_locale() {
     155    private function set_locale()
     156    {
    142157
    143158        $plugin_i18n = new Moderation_Api_i18n();
    144159
    145         $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );
     160        $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');
    146161
    147162    }
     
    154169     * @access   private
    155170     */
    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');
    162178
    163179        // 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');
    165181
    166182        // 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);
    172188
    173189
     
    177193        // Add a custom column to the comments list table
    178194        $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');
    181197
    182198    }
     
    191207     * @access   private
    192208     */
    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');
    199216
    200217        $this->loader->add_action('comment_post', $plugin_public, 'moderation_api_comment_post', 10, 2);
     
    207224    }
    208225
    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
    212252
    213253    /**
     
    216256     * @since    1.0.0
    217257     */
    218     public function run() {
     258    public function run()
     259    {
    219260        $this->loader->run();
    220261    }
     
    227268     * @return    string    The name of the plugin.
    228269     */
    229     public function get_plugin_name() {
     270    public function get_plugin_name()
     271    {
    230272        return $this->plugin_name;
    231273    }
     
    237279     * @return    Moderation_Api_Loader    Orchestrates the hooks of the plugin.
    238280     */
    239     public function get_loader() {
     281    public function get_loader()
     282    {
    240283        return $this->loader;
    241284    }
     
    247290     * @return    string    The version number of the plugin.
    248291     */
    249     public function get_version() {
     292    public function get_version()
     293    {
    250294        return $this->version;
    251295    }
    252296
    253297
    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) {
    258303            $$key = $val;
    259304        }
    260        
     305
    261306        // load_plugin_textdomain( 'modapi' );
    262307
    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');
    280343    }
    281344
     
    283346     * Display admin notices on the Comments page.
    284347     */
    285     public function add_admin_notices() {
     348    public function add_admin_notices()
     349    {
    286350        // Get the current screen
    287351        $screen = get_current_screen();
    288352
    289353        // Check if we are on the Comments admin page
    290         if ( 'edit-comments' === $screen->id ) {
     354        if ('edit-comments' === $screen->id) {
    291355            $api_key = Moderation_Api::get_api_key();
    292356
    293357            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                ));
    298362            }
    299363        }
  • moderation-api-automated-content-moderation/trunk/moderation-api.php

    r3177384 r3326772  
    1717 * Plugin URI:        https://moderationapi.com/integrations/wordpress-content-moderation
    1818 * 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.2
     19 * Version:           1.0.3
    2020 * Author:            Moderation API
    2121 * Author URI:        https://moderationapi.com/
     
    3636 * Rename this for your plugin and update it as you release new versions.
    3737 */
    38 define( 'MODERATION_API_VERSION', '1.0.2' );
     38define( 'MODERATION_API_VERSION', '1.0.3' );
    3939
    4040define( 'MODERATION_API_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     
    6262
    6363/**
     64 * The code that runs during plugin network activation for new sites.
     65 */
     66function 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
     71if (is_multisite()) {
     72    add_action('wpmu_new_blog', 'moderation_api_network_activate');
     73}
     74
     75/**
    6476 * The core plugin class that is used to define internationalization,
    6577 * admin-specific hooks, and public-facing site hooks.
  • moderation-api-automated-content-moderation/trunk/public/class-moderation-api-public.php

    r3177384 r3326772  
    2424
    2525    /**
     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    /**
    2635     * The ID of this plugin.
    2736     *
     
    5362        $this->version = $version;
    5463
    55         // Add the pre-approval filter with priority 20 (after default filters)
    5664        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);
    5866    }
    5967
     
    105113
    106114
    107     public function analyze_comment($comment){
     115    public function analyze_comment($comment, $content_id = null){
    108116        $api_key = Moderation_Api::get_api_key();
    109117
     
    113121        $user_email = sanitize_email($comment->comment_author_email);
    114122        $user_ip = sanitize_text_field($comment->comment_author_IP);
    115         // $comment_id = absint($comment->comment_ID);
    116123        $post_id = absint($comment->comment_post_ID);
    117124        $post_url = esc_url(get_permalink($post_id));
     
    124131            $authorId = $user_ip;
    125132        }
     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;
    126137
    127138        $url = Moderation_Api::API_URL . "/api/v1/moderate/text";
     
    135146            'contextId' => (string) $post_id,
    136147            'metadata' => array(
    137                 // 'comment_id' => $comment_id,
    138                 'url' => $post_url
     148                'url' => $post_url . '#comment-' . $predicted_comment_id
    139149            )
    140150        );
     151       
     152        // If content_id is provided (for edits), include it to maintain history
     153        if ($content_id) {
     154            $data['contentId'] = $content_id;
     155        }
    141156
    142157        $response = wp_remote_post($url, array(
     
    149164            // Decode the JSON response
    150165            $responseData = json_decode($body, true);
     166           
     167            // Keep contentId from API response as-is
     168           
    151169            // Return the response data
    152170            return $responseData;
     
    179197        $json = json_decode($raw_data, true);
    180198        $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           
    185213            // Sanitize and validate the action
    186214            $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : '';
     
    191219            }
    192220
    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);
    198224            }
    199225                   
     
    213239            }
    214240
     241            // Restore original blog if we switched
     242            if (is_multisite() && $site_id) {
     243                restore_current_blog();
     244            }
     245
    215246            wp_send_json_success($action);
    216247            exit();
     
    233264    public function pre_comment_approved_filter($approved, $commentdata) {
    234265        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
    235272            // Convert commentdata array to object for consistency
    236273            $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) {
    242293                    // On API failure, let other filters handle it
    243294                    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                }
    252296               
    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;
    275307
    276308           
     
    287319
    288320    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       
    289325        // 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);
    291327       
    292328        if ($analysis) {
    293329            // Store the analysis results as comment meta
    294330            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            }
    296334            // 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;
    298507        }
    299508    }
  • moderation-api-automated-content-moderation/trunk/release-plugin.sh

    r3143763 r3326772  
    55SVN_REPO=$(realpath "../moderation-api-automated-content-moderation")
    66PLUGIN_SLUG="moderation-api-automated-content-moderation"
     7SVN_USERNAME="moderationapi"
    78
    89# Ensure we're in the Git repository
     
    3435# Update SVN repository
    3536cd "$SVN_REPO" || exit 1
    36 svn update
     37svn update --username "$SVN_USERNAME"
    3738
    3839# Remove existing files in trunk
     
    5051
    5152# Add new files to SVN
    52 svn add trunk/* assets/* --force
     53svn add trunk/* assets/* --force --username "$SVN_USERNAME"
    5354
    5455# Remove deleted files from SVN
    55 svn status | grep '^\!' | sed 's/! *//' | xargs -I% svn rm %@
     56svn status | grep '^\!' | sed 's/! *//' | xargs -I% svn rm %@ --username "$SVN_USERNAME"
    5657
    5758# Commit changes to trunk
    58 svn commit -m "Update to version $VERSION"
     59svn commit -m "Update to version $VERSION" --username "$SVN_USERNAME"
    5960
    6061# Create new tag
    61 svn copy trunk "tags/$VERSION"
    62 svn commit -m "Tagging version $VERSION"
     62svn copy trunk "tags/$VERSION" --username "$SVN_USERNAME"
     63svn commit -m "Tagging version $VERSION" --username "$SVN_USERNAME"
    6364
    6465echo "Plugin $PLUGIN_SLUG version $VERSION has been successfully released!"
Note: See TracChangeset for help on using the changeset viewer.