Linux webm002.cluster126.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
/
home
/
a
/
r
/
i
/
ariannadhf
/
www
/
wp-content
/
plugins
/
simple-history
/
dropins
/
/home/a/r/i/ariannadhf/www/wp-content/plugins/simple-history/dropins/class-rss-dropin.php
<?php namespace Simple_History\Dropins; use Simple_History\Helpers; use Simple_History\Simple_History; use Simple_History\Log_Query; use Simple_History\Log_Levels; /** * Dropin Name: Global RSS Feed * Dropin URI: http://simple-history.com/ * Author: Pär Thernström */ class RSS_Dropin extends Dropin { /** * @inheritdoc */ public function loaded() { // TODO: Investigate if this include is actually needed. // get_editable_roles() is checked but never called in this file. // This might be leftover code copied from class-privacy-logger.php. // If not needed, this include should be removed. if ( ! function_exists( 'get_editable_roles' ) ) { require_once ABSPATH . '/wp-admin/includes/user.php'; } // Check the status of the RSS feed. $this->is_rss_enabled(); // Generate a rss secret, if it does not exist. if ( ! $this->get_rss_secret() ) { $this->update_rss_secret(); } add_action( 'init', array( $this, 'check_for_rss_feed_request' ) ); // Add settings with priority 15 so it' added after the main Simple History settings. add_action( 'admin_menu', array( $this, 'add_settings' ), 15 ); } /** * Add settings for the RSS feed. * * Also regenerates the secret if requested. */ public function add_settings() { // Register a setting to keep track of the RSS feed status (enabled/disabled). register_setting( Simple_History::SETTINGS_GENERAL_OPTION_GROUP, 'simple_history_enable_rss_feed', array( 'sanitize_callback' => array( Helpers::class, 'sanitize_checkbox_input', ), ) ); /** * Start new section for RSS feed. * * @var string $settings_section_rss_id ID of the section */ $settings_section_rss_id = 'simple_history_settings_section_rss'; /** * Filters the title for the feeds section headline. * * @var string $rss_section_title */ $rss_section_title = apply_filters( 'simple_history/feeds/settings_section_title', _x( 'RSS and JSON feeds', 'feeds settings headline', 'simple-history' ) ); Helpers::add_settings_section( $settings_section_rss_id, [ $rss_section_title, 'rss_feed', 'simple_history_rss_section' ], array( $this, 'settings_section_output' ), Simple_History::SETTINGS_MENU_SLUG // same slug as for options menu page. ); // Enable/Disable RSS feed. add_settings_field( 'simple_history_enable_rss_feed', Helpers::get_settings_field_title_output( __( 'Enable', 'simple-history' ), 'toggle-on' ), array( $this, 'settings_field_rss_enable' ), Simple_History::SETTINGS_MENU_SLUG, $settings_section_rss_id ); // If RSS is activated we display other fields. if ( $this->is_rss_enabled() ) { // RSS address. add_settings_field( 'simple_history_rss_feed', Helpers::get_settings_field_title_output( __( 'Address', 'simple-history' ), 'link' ), array( $this, 'settings_field_rss' ), Simple_History::SETTINGS_MENU_SLUG, $settings_section_rss_id ); // Link button to regenerate RSS secret. add_settings_field( 'simple_history_rss_feed_regenerate_secret', Helpers::get_settings_field_title_output( __( 'Regenerate', 'simple-history' ), 'autorenew' ), array( $this, 'settings_field_rss_regenerate' ), Simple_History::SETTINGS_MENU_SLUG, $settings_section_rss_id ); } // Create new RSS secret. $create_secret_nonce_name = 'simple_history_rss_secret_regenerate_nonce'; $create_nonce_ok = isset( $_GET[ $create_secret_nonce_name ] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ $create_secret_nonce_name ] ) ), 'simple_history_rss_update_secret' ); if ( $create_nonce_ok ) { $this->update_rss_secret(); // Add updated-message and store in transient and then redirect // This is the way options.php does it. $msg = __( 'Created new secret RSS address', 'simple-history' ); add_settings_error( 'simple_history_rss_feed_regenerate_secret', 'simple_history_rss_feed_regenerate_secret', $msg, 'updated' ); set_transient( 'settings_errors', get_settings_errors(), 30 ); /** * Fires after RSS secret has been updated. */ do_action( 'simple_history/rss_feed/secret_updated' ); $goback = esc_url_raw( add_query_arg( 'settings-updated', 'true', wp_get_referer() ) ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect wp_redirect( $goback ); exit; } } /** * Check if RSS feed is enabled or disabled. * * @return bool true if enabled, false if disabled */ public function is_rss_enabled() { $is_enabled = false; // User has never used the plugin we disable RSS feed. if ( $this->get_rss_secret() === false && get_option( 'simple_history_enable_rss_feed' ) === false ) { // We disable RSS by default, we use 0/1 to prevent fake disabled with bools from functions returning false for unset. update_option( 'simple_history_enable_rss_feed', '0' ); } elseif ( get_option( 'simple_history_enable_rss_feed' ) === false ) { // User was using the plugin before RSS feed became disabled by default. // We activate RSS to prevent a "breaking change". update_option( 'simple_history_enable_rss_feed', '1' ); $is_enabled = true; } elseif ( get_option( 'simple_history_enable_rss_feed' ) === '1' ) { $is_enabled = true; } return $is_enabled; } /** * Output for settings field that show current RSS address. */ public function settings_field_rss_enable() { /** * Filters the text for the RSS enable checkbox. * * @var string $enable_rss_text */ $enable_rss_text = apply_filters( 'simple_history/feeds/enable_feeds_checkbox_text', __( 'Enable feed', 'simple-history' ) ); ?> <input value="1" type="checkbox" id="simple_history_enable_rss_feed" name="simple_history_enable_rss_feed" <?php checked( $this->is_rss_enabled(), 1 ); ?> /> <label for="simple_history_enable_rss_feed"><?php echo esc_html( $enable_rss_text ); ?></label> <?php // Show premium teaser for JSON feed below the enable checkbox. echo wp_kses_post( Helpers::get_premium_feature_teaser( __( 'JSON Feed for Automation', 'simple-history' ), [ __( 'Structured data format for easy parsing', 'simple-history' ), __( 'Connect to Zapier, Make, n8n, or custom scripts', 'simple-history' ), __( 'Real-time monitoring and alerting', 'simple-history' ), ], 'premium_feeds_settings', __( 'Enable JSON Feed', 'simple-history' ) ) ); } /** * Check if current request is a request for the RSS feed. */ public function check_for_rss_feed_request() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['simple_history_get_rss'] ) ) { $this->output_rss(); exit; } } /** * Get the RSS secret. * * @return bool|string RSS secret or false if not set. */ public function get_rss_secret() { return get_option( 'simple_history_rss_secret' ); } /** * Output RSS. */ public function output_rss() { $rss_secret_option = get_option( 'simple_history_rss_secret' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $rss_secret_get = sanitize_text_field( wp_unslash( $_GET['rss_secret'] ?? '' ) ); if ( empty( $rss_secret_option ) || empty( $rss_secret_get ) ) { die(); } /** @var bool $rss_show */ $rss_show = true; /** * Filter if RSS feed should be shown or not. * Default is true. * @since 1.3.8 * @param bool $rss_show */ $rss_show = apply_filters( 'simple_history/rss_feed_show', $rss_show ); if ( ! $rss_show || ! $this->is_rss_enabled() ) { wp_die( 'Nothing here.' ); } header( 'Content-Type: text/xml; charset=utf-8' ); echo '<?xml version="1.0" encoding="UTF-8"?>'; $self_link = $this->get_rss_address(); $title = sprintf( /* translators: %s blog name */ __( 'History for %s', 'simple-history' ), get_bloginfo( 'name' ), ); $description = sprintf( /* translators: %s blog name */ esc_html__( 'WordPress History for %s', 'simple-history' ), get_bloginfo( 'name' ) ); if ( $rss_secret_option === $rss_secret_get ) { echo PHP_EOL; ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title><?php echo esc_xml( $title ); ?></title> <description><?php echo esc_xml( $description ); ?></description> <link><?php echo esc_url( get_bloginfo( 'url' ) ); ?></link> <atom:link href="<?php echo esc_url( $self_link ); ?>" rel="self" type="application/atom+xml" /> <?php // Override capability check: if you have a valid rss_secret_key you can read it all. $action_tag = 'simple_history/loggers_user_can_read/can_read_single_logger'; add_filter( $action_tag, '__return_true', 10, 0 ); // Modify header time output so it does not show relative date or time ago-format // Because we don't know when a user reads the RSS feed, time ago format may be very inaccurate. add_filter( 'simple_history/header_just_now_max_time', '__return_zero' ); add_filter( 'simple_history/header_time_ago_max_time', '__return_zero' ); // Set args from query string. // phpcs:ignore WordPress.Security.NonceVerification.Recommended $args = $this->set_log_query_args_from_query_string( $_GET ); // Default to last 7 days when no date filter is provided, // to avoid scanning the entire events table. $has_date_filter = ! empty( $args['date_from'] ) || ! empty( $args['date_to'] ) || ! empty( $args['dates'] ); if ( ! $has_date_filter ) { $args['dates'] = 'lastdays:7'; } // RSS feeds don't need pagination metadata. $args['skip_count_query'] = true; // Occasion grouping is enabled so subsequentOccasions // values are accurate in the RSS output. /** * Filters the arguments passed to `SimpleHistoryLogQuery()` when fetching the RSS feed * * @example Change number of posts to retrieve in RSS feed. * * // This example changes the number of posts in the RSS feed to 50 from the default 10. * * ```php * add_filter( * 'simple_history/rss_feed_args', * function( $args ) { * $args['posts_per_page'] = 50; * return $args; * } * ); * * @example Change number of posts to retrieve in RSS feed. * * // This example changes the number of posts in the RSS feed to 20 from the default 10. * * ```php * add_filter( * 'simple_history/rss_feed_args', * function( $args ) { * $args['posts_per_page'] = 20; * return $args; * } * ); * * @param array $args SimpleHistoryLogQuery arguments. * @return array */ $args = apply_filters( 'simple_history/rss_feed_args', $args ); $logQuery = new Log_Query(); $queryResults = $logQuery->query( $args ); // Remove capability override after query is done // remove_action( $action_tag, '__return_true', 10 );. if ( is_wp_error( $queryResults ) ) { $queryResults = array( 'log_rows' => array() ); } foreach ( $queryResults['log_rows'] as $row ) { $header_output = $this->clean_broken_links( $this->simple_history->get_log_row_header_output( $row ) ); $text_output = $this->clean_broken_links( $this->simple_history->get_log_row_plain_text_output( $row ) ); $details_output = $this->clean_broken_links( (string) $this->simple_history->get_log_row_details_output( $row ) ); // phpcs:ignore Squiz.PHP.CommentedOutCode.Found -- URL reference. // See http://cyber.law.harvard.edu/rss/rss.html#ltguidgtSubelementOfLtitemgt. $item_guid = esc_url( add_query_arg( 'SimpleHistoryGuid', $row->id, home_url() ) ); $item_link = esc_url( add_query_arg( 'SimpleHistoryGuid', $row->id, home_url() ) ); /** * Filter the guid/link URL used in RSS feed. * Link will be esc_url'ed by simple history, so no need to do that in your filter * * @since 2.0.23 * * @param string $item_guid link. * @param object $row */ $item_link = apply_filters( 'simple_history/rss_item_link', $item_link, $row ); $item_link = esc_url( $item_link ); $item_title = sprintf( '%2$s', Log_Levels::get_log_level_translated( $row->level ), wp_kses( $text_output, array() ) ); $level_output = sprintf( // translators: %s is the severity level of the log. esc_html__( 'Severity level: %1$s', 'simple-history' ), Log_Levels::get_log_level_translated( $row->level ) ); $wp_kses_attrs = array( 'a' => array( 'href' => array(), 'class' => array(), 'data-ip-address' => array(), 'target' => array(), 'title' => array(), ), 'em' => array(), 'span' => array( 'class' => array(), 'title' => array(), 'aria-hidden' => array(), ), 'time' => array( 'datetime' => array(), 'class' => array(), ), 'strong' => array( 'class' => array(), ), 'div' => array( 'class' => array(), 'tabindex' => array(), ), 'p' => array(), 'del' => array(), 'ins' => array(), 'table' => array( 'class' => array(), ), 'tbody' => array(), 'tr' => array(), 'td' => array( 'class' => array(), ), 'col' => array( 'class' => array(), ), ); ?> <item> <title><?php echo esc_xml( $item_title ); ?></title> <description><![CDATA[ <p><?php echo wp_kses( $header_output, $wp_kses_attrs ); ?></p> <p><?php echo wp_kses( $text_output, $wp_kses_attrs ); ?></p> <div><?php echo wp_kses( $details_output, $wp_kses_attrs ); ?></div> <p><?php echo wp_kses( $level_output, $wp_kses_attrs ); ?></p> <?php // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $occasions = $row->subsequentOccasions - 1; if ( $occasions ) { echo '<p>'; echo esc_html( sprintf( // translators: %1$s is the number of times this log has been repeated. _n( '+%1$s occasion', '+%1$s occasions', $occasions, 'simple-history' ), (int) $occasions ) ); echo '</p>'; } ?> ]]></description> <?php // author must be email to validate, but the field is optional, so we skip it. /* <author><?php echo $row->initiator ?></author> */ ?> <pubDate><?php echo esc_xml( gmdate( 'D, d M Y H:i:s', strtotime( $row->date ) ) ); ?> GMT</pubDate> <guid isPermaLink="false"><![CDATA[<?php echo esc_xml( $item_guid ); ?>]]></guid> <link><![CDATA[<?php echo esc_url( $item_link ); ?>]]></link> </item> <?php } ?> </channel> </rss> <?php } else { // RSS secret was not ok. echo PHP_EOL; ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title><?php echo esc_xml( $title ); ?></title> <description><?php echo esc_xml( $description ); ?></description> <link><?php echo esc_url( home_url() ); ?></link> <item> <title><?php echo esc_xml( __( 'Wrong RSS secret', 'simple-history' ) ); ?></title> <description><?php echo esc_xml( __( 'Your RSS secret for Simple History RSS feed is wrong. Please see WordPress settings for current link to the RSS feed.', 'simple-history' ) ); ?></description> <pubDate><?php echo esc_xml( gmdate( 'D, d M Y H:i:s', time() ) ); ?> GMT</pubDate> <guid><?php echo esc_url( add_query_arg( 'SimpleHistoryGuid', 'wrong-secret', home_url() ) ); ?></guid> </item> </channel> </rss> <?php } } /** * Clean broken links from HTML output. * * Finds <a> tags with unresolved {placeholder} hrefs or empty hrefs * and removes the href attribute. An <a> without href is valid HTML * and renders as plain text with no link behavior. * * @since 5.x * @param string $html HTML output from a logger. * @return string Cleaned HTML with broken links neutralized. */ private function clean_broken_links( $html ) { $processor = new \WP_HTML_Tag_Processor( $html ); while ( $processor->next_tag( array( 'tag_name' => 'a' ) ) ) { $href = $processor->get_attribute( 'href' ); $is_empty = $href === '' || $href === null; $is_placeholder = is_string( $href ) && str_contains( $href, '{' ); if ( ! $is_empty && ! $is_placeholder ) { continue; } $processor->remove_attribute( 'href' ); } return $processor->get_updated_html(); } /** * Create a new RSS secret. * * @return string new secret */ public function update_rss_secret() { $rss_secret = ''; for ( $i = 0; $i < 20; $i++ ) { $rss_secret .= chr( random_int( 97, 122 ) ); } update_option( 'simple_history_rss_secret', $rss_secret ); return $rss_secret; } /** * Output for settings field that show current RSS address. */ public function settings_field_rss() { echo '<p class="simple_history_rss_feed_query_parameters">'; echo wp_kses( sprintf( /* translators: %s is a link to the documentation */ __( 'Query parameters can be used to control what to include in the feed. <a href="%1$s" class="sh-ExternalLink" target="_blank">View documentation</a>.', 'simple-history' ), esc_url( Helpers::get_tracking_url( 'https://simple-history.com/docs/feeds/', 'docs_rss_help' ) ) ), [ 'a' => [ 'href' => [], 'target' => [], 'class' => [], ], ] ); echo '</p>'; echo '<br />'; printf( ' <p> <strong> %1$s </strong> </p> ', esc_html__( 'RSS feed', 'simple-history' ) // 1 ); printf( '<p> <code> <a id="simple_history_rss_feed_address" href="%1$s">%1$s</a> </code> </p>', esc_url( $this->get_rss_address() ) ); /** * Fires after the RSS address has been output. * * @param RSS_Dropin $instance */ do_action( 'simple_history/feeds/after_address', $this ); } /** * Output for settings field that regenerates the RSS address/secret */ public function settings_field_rss_regenerate() { $update_link = esc_url( add_query_arg( '', '' ) ); $update_link = wp_nonce_url( $update_link, 'simple_history_rss_update_secret', 'simple_history_rss_secret_regenerate_nonce' ); echo '<p>'; esc_html_e( 'You can generate a new secret for the feeds. This is useful if you think that the address has fallen into the wrong hands.', 'simple-history' ); echo '</p>'; echo '<p>'; printf( '<a class="button" href="%1$s">%2$s</a>', esc_url( $update_link ), // 1 esc_html__( 'Generate new address', 'simple-history' ) // 2 ); echo '</p>'; } /** * Get the URL to the RSS feed. * * @return string URL */ public function get_rss_address() { $rss_secret = get_option( 'simple_history_rss_secret' ); return add_query_arg( array( 'simple_history_get_rss' => '1', 'rss_secret' => $rss_secret, ), get_bloginfo( 'url' ) . '/' ); } /** * Content for section intro. Leave it be, even if empty. * Called from add_sections_setting. */ public function settings_section_output() { ?> <p> <strong><?php esc_html_e( 'Monitor your site activity in real-time with feeds.', 'simple-history' ); ?></strong> </p> <p> <?php esc_html_e( 'Get updates on logins, content changes, plugin activity and more—delivered to your feed reader or monitoring tools. Perfect for staying informed without constantly checking your dashboard.', 'simple-history' ); ?> </p> <p> <?php esc_html_e( 'Make sure you only share the feeds with people you trust, since they can contain sensitive or confidential information.', 'simple-history' ); ?> </p> <?php /** * Allow premium to add additional feed information. * * @since 4.0 */ do_action( 'simple_history/feeds/settings_section_description' ); } /** * Update log query args from query string. * * @param array $args Query string from $_GET. * @return array Updated log query args. */ public function set_log_query_args_from_query_string( $args ) { $posts_per_page = isset( $args['posts_per_page'] ) ? (int) $args['posts_per_page'] : 10; $paged = isset( $args['paged'] ) ? (int) $args['paged'] : 1; $date_from = isset( $args['date_from'] ) ? sanitize_text_field( $args['date_from'] ) : null; $date_to = isset( $args['date_to'] ) ? sanitize_text_field( $args['date_to'] ) : null; $loggers = isset( $args['loggers'] ) ? sanitize_text_field( $args['loggers'] ) : null; $messages = isset( $args['messages'] ) ? sanitize_text_field( $args['messages'] ) : null; $loglevels = isset( $args['loglevels'] ) ? sanitize_text_field( $args['loglevels'] ) : null; $dates = isset( $args['dates'] ) ? sanitize_text_field( $args['dates'] ) : null; // Exclusion filters - useful for subscribing to events excluding your own actions. $exclude_loggers = isset( $args['exclude_loggers'] ) ? sanitize_text_field( $args['exclude_loggers'] ) : null; $exclude_messages = isset( $args['exclude_messages'] ) ? sanitize_text_field( $args['exclude_messages'] ) : null; $exclude_loglevels = isset( $args['exclude_loglevels'] ) ? sanitize_text_field( $args['exclude_loglevels'] ) : null; $exclude_user = isset( $args['exclude_user'] ) ? (int) $args['exclude_user'] : null; $exclude_users = isset( $args['exclude_users'] ) ? sanitize_text_field( $args['exclude_users'] ) : null; return [ 'posts_per_page' => $posts_per_page, 'paged' => $paged, 'date_from' => $date_from, 'date_to' => $date_to, 'loggers' => $loggers, 'messages' => $messages, 'loglevels' => $loglevels, 'dates' => $dates, 'exclude_loggers' => $exclude_loggers, 'exclude_messages' => $exclude_messages, 'exclude_loglevels' => $exclude_loglevels, 'exclude_user' => $exclude_user, 'exclude_users' => $exclude_users, ]; } }