p =& $plugin; if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * This hook is fired once WordPress, plugins, and the theme are fully loaded and instantiated. */ add_action( 'wp_loaded', array( $this, 'add_wp_callbacks' ) ); } /* * Add WordPress action and filter callbacks. */ public function add_wp_callbacks() { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * Since WPSSO Core v17.0.0. * * Register our comment meta. */ $this->register_meta( $object_type = 'comment', WPSSO_META_NAME ); /* * Called by wp_insert_comment(), which is called by wp_new_comment(). */ add_action( 'wp_insert_comment', array( $this, 'refresh_cache_insert_comment' ), PHP_INT_MAX, 2 ); /* * Called by wp_transition_comment_status(). */ add_action( 'transition_comment_status', array( $this, 'refresh_cache_comment_status' ), PHP_INT_MAX, 3 ); } /* * Get the $mod object for a comment ID. */ public function get_mod( $comment_id ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark_caller(); $this->p->debug->log_args( array( 'comment_id' => $comment_id, ) ); } static $local_fifo = array(); /* * Maybe return the array from the local cache. */ if ( isset( $local_fifo[ $comment_id ] ) ) { if ( ! $this->md_cache_disabled ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: comment id ' . $comment_id . ' mod array from local cache' ); } return $local_fifo[ $comment_id ]; } else unset( $local_fifo[ $comment_id ] ); } /* * Maybe limit the number of array elements. */ $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX ); $mod = self::get_mod_defaults(); /* * Common elements. */ $mod[ 'id' ] = is_numeric( $comment_id ) ? (int) $comment_id : 0; // Cast as integer. $mod[ 'name' ] = 'comment'; $mod[ 'name_transl' ] = _x( 'comment', 'module name', 'wpsso' ); $mod[ 'obj' ] =& $this; /* * WpssoComment elements. */ $mod[ 'is_comment' ] = true; if ( $mod[ 'id' ] ) { // Just in case. $mod[ 'wp_obj' ] = SucomUtilWP::get_comment_object( $mod[ 'id' ] ); if ( $mod[ 'wp_obj' ] instanceof WP_Comment ) { // Just in case. $mod[ 'comment_author' ] = (int) $mod[ 'wp_obj' ]->user_id; // Comment author user ID. $mod[ 'comment_author_name' ] = $mod[ 'wp_obj' ]->comment_author; // Comment author name. $mod[ 'comment_author_url' ] = $mod[ 'wp_obj' ]->comment_author_url; $mod[ 'comment_parent' ] = $mod[ 'wp_obj' ]->comment_parent; $mod[ 'comment_time' ] = mysql2date( 'c', $mod[ 'wp_obj' ]->comment_date_gmt ); // ISO 8601 date. $mod[ 'comment_timestamp' ] = mysql2date( 'U', $mod[ 'wp_obj' ]->comment_date_gmt ); // Unix timestamp. $mod[ 'is_public' ] = $mod[ 'wp_obj' ]->comment_approved ? true : false; $comment_rating = self::get_meta( $mod[ 'id' ], WPSSO_META_RATING_NAME, $single = true ); if ( is_numeric( $comment_rating ) ) { $mod[ 'comment_rating' ] = $comment_rating; } } else $mod[ 'wp_obj' ] = false; } /* * Filter the comment mod array. */ $mod = apply_filters( 'wpsso_get_comment_mod', $mod, $comment_id ); if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'mod', $mod ); } /* * Maybe save the array to the local cache. */ if ( ! $this->md_cache_disabled ) { $local_fifo[ $comment_id ] = $mod; if ( $this->p->debug->enabled ) { $this->p->debug->log_size( 'local_fifo', $local_fifo ); } } return $mod; } public function get_mod_wp_object( array $mod ) { if ( $mod[ 'wp_obj' ] instanceof WP_Comment ) { return $mod[ 'wp_obj' ]; } return SucomUtilWP::get_comment_object( $mod[ 'id' ] ); } /* * Option handling methods: * * get_defaults() * get_options() * save_options() * delete_options() */ public function get_options( $comment_id, $md_key = false, $filter_opts = true, $merge_defs = false ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark_caller(); $this->p->debug->log_args( array( 'comment_id' => $comment_id, 'md_key' => $md_key, 'filter_opts' => $filter_opts, 'merge_defs' => $merge_defs, ) ); } static $local_fifo = array(); /* * Use $comment_id and $filter_opts to create the cache ID string, but do not add $merge_defs. */ $cache_id = SucomUtil::get_assoc_salt( array( 'id' => $comment_id, 'filter' => $filter_opts ) ); /* * Maybe initialize a new local cache element. Use isset() instead of empty() to allow for an empty array. */ if ( ! isset( $local_fifo[ $cache_id ] ) ) { /* * Maybe limit the number of array elements. */ $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX ); $local_fifo[ $cache_id ] = null; // Create an element to reference. } $md_opts =& $local_fifo[ $cache_id ]; // Reference the local cache element. if ( null === $md_opts ) { // Maybe read metadata into a new local cache element. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting metadata for comment id ' . $comment_id ); } $md_opts = self::get_meta( $comment_id, WPSSO_META_NAME, $single = true ); if ( ! is_array( $md_opts ) ) { $md_opts = array(); // WPSSO_META_NAME not found. } unset( $md_opts[ 'opt_filtered' ] ); // Just in case. /* * Check if options need to be upgraded and saved. */ if ( $this->p->opt->is_upgrade_required( $md_opts ) ) { $md_opts = $this->upgrade_options( $md_opts, $comment_id ); self::update_meta( $comment_id, WPSSO_META_NAME, $md_opts ); } } if ( $filter_opts ) { if ( ! empty( $md_opts[ 'opt_filtered' ] ) ) { // Set before calling filters to prevent recursion. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping filters: options already filtered' ); } } else { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'setting opt_filtered to 1' ); } $md_opts[ 'opt_filtered' ] = 1; // Set before calling filters to prevent recursion. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'required call to WpssoComment->get_mod() for comment ID ' . $comment_id ); } $mod = $this->get_mod( $comment_id ); $filter_name = 'wpsso_get_md_options'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } $md_opts = apply_filters( $filter_name, $md_opts, $mod ); /* * Hooked by several integration modules to provide information about the current content. * e-Commerce integration modules will provide information on their product (price, * condition, etc.) and disable these options in the Document SSO metabox. */ $filter_name = 'wpsso_get_' . $mod[ 'name' ] . '_options'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } $md_opts = apply_filters( $filter_name, $md_opts, $comment_id, $mod ); $filter_name = 'wpsso_sanitize_md_options'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } $md_opts = apply_filters( $filter_name, $md_opts, $mod ); } } /* * Maybe save the array to the local cache. */ if ( $this->md_cache_disabled ) { $deref_md_opts = $local_fifo[ $cache_id ]; // Dereference. unset( $local_fifo, $md_opts ); return $this->return_options( $comment_id, $deref_md_opts, $md_key, $merge_defs ); } return $this->return_options( $comment_id, $md_opts, $md_key, $merge_defs ); } /* * Use $rel = false to extend WpssoAbstractWpMeta->save_options(). */ public function save_options( $comment_id, $rel = false ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'comment_id' => $comment_id, ) ); } if ( empty( $comment_id ) ) { // Just in case. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: comment id is empty' ); } return; } elseif ( ! $this->verify_submit_nonce() ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: verify_submit_nonce failed' ); } return; /* * Check user capability for the comment id. */ } elseif ( ! $this->user_can_edit( $comment_id ) ) { if ( $this->p->debug->enabled ) { $user_id = get_current_user_id(); $this->p->debug->log( 'exiting early: user id ' . $user_id . ' cannot edit comment id ' . $comment_id ); } return; } $this->md_cache_disable(); // Disable the local cache. $mod = $this->get_mod( $comment_id ); $md_opts = $this->get_submit_opts( $mod ); // Merge previous + submitted options and then sanitize. $this->md_cache_enable(); // Re-enable the local cache. if ( false === $md_opts ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: returned submit options is false' ); } return; } $md_opts = apply_filters( 'wpsso_save_md_options', $md_opts, $mod ); $md_opts = apply_filters( 'wpsso_save_' . $mod[ 'name' ] . '_options', $md_opts, $comment_id, $mod ); return self::update_meta( $comment_id, WPSSO_META_NAME, $md_opts ); } /* * Use $rel = false to extend WpssoAbstractWpMeta->delete_options(). */ public function delete_options( $comment_id, $rel = false ) { return self::delete_meta( $comment_id, WPSSO_META_NAME ); } public function refresh_cache_insert_comment( $comment_id, $comment ) { if ( ! empty( $comment->comment_approved ) ) { if ( ! empty( $comment->comment_post_ID ) ) { $this->p->post->refresh_cache( $comment->comment_post_ID ); // Refresh the cache for a single post ID. } } } public function refresh_cache_comment_status( $new_status, $old_status, $comment ) { if ( 'approved' === $new_status || 'approved' === $old_status ) { if ( ! empty( $comment->comment_post_ID ) ) { $this->p->post->refresh_cache( $comment->comment_post_ID ); // Refresh the cache for a single post ID. } } } /* * Retrieves or updates the metadata cache by key and group. */ public function get_update_meta_cache( $comment_id ) { return SucomUtilWP::get_update_meta_cache( $comment_id, $meta_type = 'comment' ); } /* * Check user capability for the comment id. * * Use $rel = false to extend WpssoAbstractWpMeta->user_can_edit(). */ public function user_can_edit( $comment_id, $rel = false ) { $capability = 'edit_comment'; if ( ! current_user_can( $capability, $comment_id ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: cannot ' . $capability . ' for comment id ' . $comment_id ); } /* * Add notice only if the admin notices have not already been shown. */ if ( $this->p->notice->is_admin_pre_notices() ) { $this->p->notice->err( sprintf( __( 'Insufficient privileges to edit comment ID %1$s.', 'wpsso' ), $comment_id ) ); } return false; } return true; } /* * If $meta_key is en empty string, retrieves all metadata for the specified object ID. * * See https://developer.wordpress.org/reference/functions/get_metadata/. */ public static function get_meta( $comment_id, $meta_key = '', $single = false ) { return get_metadata( 'comment', $comment_id, $meta_key, $single ); } public static function update_meta( $comment_id, $meta_key, $value ) { return update_metadata( 'comment', $comment_id, $meta_key, $value ); } public static function delete_meta( $comment_id, $meta_key ) { return delete_metadata( 'comment', $comment_id, $meta_key ); } } }