Whoops \ Exception \ ErrorException (E_WARNING)
Undefined variable $disclaimers Whoops\Exception\ErrorException thrown with message "Undefined variable $disclaimers" Stacktrace: #14 Whoops\Exception\ErrorException in /data/websites/webnews/web/app/plugins/affiliation/src/Rendering/Disclaimer.php:115 #13 Whoops\Run:handleError in /data/websites/webnews/web/app/plugins/affiliation/src/Rendering/Disclaimer.php:115 #12 Blazemedia\Affiliation\Rendering\Disclaimer:printCustomDisclaimers in /data/websites/webnews/web/app/plugins/affiliation/src/Rendering/Disclaimer.php:58 #11 Blazemedia\Affiliation\Rendering\Disclaimer:printDisclaimer in /data/websites/webnews/web/wp/wp-includes/class-wp-hook.php:324 #10 WP_Hook:apply_filters in /data/websites/webnews/web/wp/wp-includes/plugin.php:205 #9 apply_filters in /data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Content/SplittedContent.php:50 #8 THEME\ViewModel\Post\Content\SplittedContent:init in /data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Content/SplittedContent.php:25 #7 THEME\ViewModel\Post\Content\SplittedContent:__construct in /data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Templates/SingleSplittedContent.php:30 #6 THEME\ViewModel\Post\Templates\SingleSplittedContent:getContent in /data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Templates/Single.php:23 #5 THEME\ViewModel\Post\Templates\Single:getPostData in /data/websites/webnews/web/app/themes/webnews/src/ViewModel/Single.php:55 #4 THEME\ViewModel\Single:setPost in /data/websites/webnews/web/app/themes/webnews/src/ViewModel/Single.php:28 #3 THEME\ViewModel\Single:__construct in /data/websites/webnews/web/app/themes/webnews/single.php:16 #2 include in /data/websites/webnews/web/wp/wp-includes/template-loader.php:106 #1 require_once in /data/websites/webnews/web/wp/wp-blog-header.php:19 #0 require in /data/websites/webnews/web/index.php:6
Stack frames (15)
14
Whoops\Exception\ErrorException
/web/app/plugins/affiliation/src/Rendering/Disclaimer.php115
13
Whoops\Run handleError
/web/app/plugins/affiliation/src/Rendering/Disclaimer.php115
12
Blazemedia\Affiliation\Rendering\Disclaimer printCustomDisclaimers
/web/app/plugins/affiliation/src/Rendering/Disclaimer.php58
11
Blazemedia\Affiliation\Rendering\Disclaimer printDisclaimer
/web/wp/wp-includes/class-wp-hook.php324
10
WP_Hook apply_filters
/web/wp/wp-includes/plugin.php205
9
apply_filters
/web/app/themes/webnews/src/ViewModel/Post/Content/SplittedContent.php50
8
THEME\ViewModel\Post\Content\SplittedContent init
/web/app/themes/webnews/src/ViewModel/Post/Content/SplittedContent.php25
7
THEME\ViewModel\Post\Content\SplittedContent __construct
/web/app/themes/webnews/src/ViewModel/Post/Templates/SingleSplittedContent.php30
6
THEME\ViewModel\Post\Templates\SingleSplittedContent getContent
/web/app/themes/webnews/src/ViewModel/Post/Templates/Single.php23
5
THEME\ViewModel\Post\Templates\Single getPostData
/web/app/themes/webnews/src/ViewModel/Single.php55
4
THEME\ViewModel\Single setPost
/web/app/themes/webnews/src/ViewModel/Single.php28
3
THEME\ViewModel\Single __construct
/web/app/themes/webnews/single.php16
2
include
/web/wp/wp-includes/template-loader.php106
1
require_once
/web/wp/wp-blog-header.php19
0
require
/web/index.php6
/data/websites/webnews/web/app/plugins/affiliation/src/Rendering/Disclaimer.php
   
        $disclaimerContainer = <<<HTML
            <div class="blz_custom_disclaimer">{{ disclaimer_default }}{{ disclaimers }}</div> 
        HTML;
 
        $disclaimerMarkup = <<<HTML
            {{ disclaimer }}
        HTML;
 
        if (!is_user_logged_in()) {
            $disclaimerContainer .= $errorStyle;
        };
 
        $disclaimerContainer = str_replace('{{ disclaimer_default }}', html_entity_decode($this->defaultDisclaimer) ."<br><br>", $disclaimerContainer );
 
        $disclaimers .= array_reduce( $this->disclaimers, function( $markup, $disclaimer) use ( $disclaimerMarkup ) {
 
            return $markup . str_replace('{{ disclaimer }}', html_entity_decode($disclaimer)."<br><br>", $disclaimerMarkup );
 
        }, '');
 
        return $content . str_replace('{{ disclaimers }}', $disclaimers, $disclaimerContainer );
 
    }
 
 
    /**
     * Metodo pubblico per l'accesso all'istanza unica di classe.
     * @return object|
     */
    public static function getInstance() {
 
        if ( !isset( self::$instance ) ) {
            
            self::$instance = new Disclaimer();
        }
        
        return self::$instance;
    }
 
/data/websites/webnews/web/app/plugins/affiliation/src/Rendering/Disclaimer.php
   
        $disclaimerContainer = <<<HTML
            <div class="blz_custom_disclaimer">{{ disclaimer_default }}{{ disclaimers }}</div> 
        HTML;
 
        $disclaimerMarkup = <<<HTML
            {{ disclaimer }}
        HTML;
 
        if (!is_user_logged_in()) {
            $disclaimerContainer .= $errorStyle;
        };
 
        $disclaimerContainer = str_replace('{{ disclaimer_default }}', html_entity_decode($this->defaultDisclaimer) ."<br><br>", $disclaimerContainer );
 
        $disclaimers .= array_reduce( $this->disclaimers, function( $markup, $disclaimer) use ( $disclaimerMarkup ) {
 
            return $markup . str_replace('{{ disclaimer }}', html_entity_decode($disclaimer)."<br><br>", $disclaimerMarkup );
 
        }, '');
 
        return $content . str_replace('{{ disclaimers }}', $disclaimers, $disclaimerContainer );
 
    }
 
 
    /**
     * Metodo pubblico per l'accesso all'istanza unica di classe.
     * @return object|
     */
    public static function getInstance() {
 
        if ( !isset( self::$instance ) ) {
            
            self::$instance = new Disclaimer();
        }
        
        return self::$instance;
    }
 
/data/websites/webnews/web/app/plugins/affiliation/src/Rendering/Disclaimer.php
        if( trim($disclaimer) == '' || in_array( $disclaimer, $this->disclaimers ) ) return;
 
        $this->disclaimers[] = $disclaimer;
    }
 
    public function getStatus() : bool {
        return $this->status;
    }
 
    public function setStatus( bool $status ) : void {
        $this->status = $status;
    }
 
    public function printDisclaimer( $content ) {
 
        if( !$this->status ) return $content;
 
        if( $this->hasNoDisclaimerTag() || $this->isNoDisclaimerFlagOn() ) return $content;
 
        return empty( $this->disclaimers ) ? $this->printDefaultDisclaimer( $content ) : $this->printCustomDisclaimers( $content);
    }
 
    
    private function isNoDisclaimerFlagOn() {
 
        $has_disclaimer = get_post_meta( $this->post->ID, 'has_disclaimer', true );
 
        return !empty($has_disclaimer) && $has_disclaimer == "no";
    }
 
 
    private function hasNoDisclaimerTag(){
 
        return has_tag('no-disclaimer', $this->post );
    }
 
    private function printDefaultDisclaimer( $content ) {
 
        $errorStyle = <<<HTML
        <style>.bmaff_error { display: none !important;}</style>
/data/websites/webnews/web/wp/wp-includes/class-wp-hook.php
 
        $this->iterations[ $nesting_level ] = $this->priorities;
 
        $num_args = count( $args );
 
        do {
            $this->current_priority[ $nesting_level ] = current( $this->iterations[ $nesting_level ] );
 
            $priority = $this->current_priority[ $nesting_level ];
 
            foreach ( $this->callbacks[ $priority ] as $the_ ) {
                if ( ! $this->doing_action ) {
                    $args[0] = $value;
                }
 
                // Avoid the array_slice() if possible.
                if ( 0 === $the_['accepted_args'] ) {
                    $value = call_user_func( $the_['function'] );
                } elseif ( $the_['accepted_args'] >= $num_args ) {
                    $value = call_user_func_array( $the_['function'], $args );
                } else {
                    $value = call_user_func_array( $the_['function'], array_slice( $args, 0, $the_['accepted_args'] ) );
                }
            }
        } while ( false !== next( $this->iterations[ $nesting_level ] ) );
 
        unset( $this->iterations[ $nesting_level ] );
        unset( $this->current_priority[ $nesting_level ] );
 
        --$this->nesting_level;
 
        return $value;
    }
 
    /**
     * Calls the callback functions that have been added to an action hook.
     *
     * @since 4.7.0
     *
     * @param array $args Parameters to pass to the callback functions.
/data/websites/webnews/web/wp/wp-includes/plugin.php
        $all_args = func_get_args(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
        _wp_call_all_hook( $all_args );
    }
 
    if ( ! isset( $wp_filter[ $hook_name ] ) ) {
        if ( isset( $wp_filter['all'] ) ) {
            array_pop( $wp_current_filter );
        }
 
        return $value;
    }
 
    if ( ! isset( $wp_filter['all'] ) ) {
        $wp_current_filter[] = $hook_name;
    }
 
    // Pass the value to WP_Hook.
    array_unshift( $args, $value );
 
    $filtered = $wp_filter[ $hook_name ]->apply_filters( $value, $args );
 
    array_pop( $wp_current_filter );
 
    return $filtered;
}
 
/**
 * Calls the callback functions that have been added to a filter hook, specifying arguments in an array.
 *
 * @since 3.0.0
 *
 * @see apply_filters() This function is identical, but the arguments passed to the
 *                      functions hooked to `$hook_name` are supplied using an array.
 *
 * @global WP_Hook[] $wp_filter         Stores all of the filters and actions.
 * @global int[]     $wp_filters        Stores the number of times each filter was triggered.
 * @global string[]  $wp_current_filter Stores the list of current filters with the current one last.
 *
 * @param string $hook_name The name of the filter hook.
 * @param array  $args      The arguments supplied to the functions hooked to `$hook_name`.
/data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Content/SplittedContent.php
     *
     * @return Array sections
     */
    public function getSections() {
 
        return $this->sections;
    }
 
    
    private function init($content) {   
 
        // load all the splitters
        $this->getAvailableSplitters();
 
        // cambiare gli shortcode "mutati"
        foreach($this->splitters as $splitter)
            $content = $splitter->convertShortCode($content);
 
        // applicare gli shortcode "reali"
        $content = apply_filters('the_content', $content);
 
        // initialize the content as a big section to split
        $this->sections = [
            [ 'type' => 'content', 'content' => $content ]
        ];
 
        foreach ($this->splitters as $splitter) 
            $this->splitSections($splitter);
        
    }
 
    
    /**
     * Return the list of the splitters
     */
    private function getAvailableSplitters(){
 
        // una reflection un po' accroccata ma funzionale
        $composer = require get_template_directory() . '/vendor/composer/autoload_classmap.php';
 
/data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Content/SplittedContent.php
 * @param content:string     - is the content to split
 * @param post_id:int        - the current post id
 *
 * getSections()     - Array with the sequence of content sections to print
 *                             each one indentified with a type and a content
 *
 * splitContent()    - activate the splitter on a new content
 */
class SplittedContent
{
 
    private $splitters;
    private $sections = [];
    private $post_id  = 0;
    
    function __construct($post)
    {
        $this->post_id = $post->ID;
 
        $this->init($post->post_content);
    }
 
 
    /**
     * Expose the sections
     *
     * @return Array sections
     */
    public function getSections() {
 
        return $this->sections;
    }
 
    
    private function init($content) {   
 
        // load all the splitters
        $this->getAvailableSplitters();
 
        // cambiare gli shortcode "mutati"
/data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Templates/SingleSplittedContent.php
     *
    public function getPostData($post) {
 
        $processedPost = parent::getPostData($post);
        
        $processedPost['content'] = $this->getContent($post);
 
        return $processedPost;            
    }
    */
 
    /**
     * Returns the content as a list (array) of splitted sections
     * 
     * @param WP_Post $post
     * @return array
     */
    protected function getContent( $post ) {
 
        return ( new SplittedContent( $post ) )->getSections();
    }
}
 
/data/websites/webnews/web/app/themes/webnews/src/ViewModel/Post/Templates/Single.php
 
use THEME\ViewModel\Post\Content\FeaturedImage;
 
class Single implements iTemplate {    
    
 
    /**
     * Basic post data for a generic Single
     *
     * @param WP_Post $post
     * @return array
     */
    public function getPostData( $post ) {
 
        /// basic post data 
        $postData = [
            'id'      => $post->ID,
            'title'   => $post->post_title,
            'excerpt' => $post->post_excerpt,
            'content' => $this->getContent( $post ),
            'image'   => $this->getImage( $post ),
            'pubdate' => $this->getDate( $post ),
            'moddate' => $this->getUpdateDate( $post ),
            'tags'    => $this->getTags( $post ),
            'link'    => get_the_permalink( $post ),
        ];
 
        /// add post_meta to basic data
        $customFields = $this->getCustomFields( $post );
 
        if( is_array( $customFields ) && !empty( $customFields ) ) {
 
            $postData = array_merge( $postData, $customFields );
        }
 
        return $postData;
    }
 
 
    /**
/data/websites/webnews/web/app/themes/webnews/src/ViewModel/Single.php
 
    }
 
        
    /**
     * Writes the basic page info
     */
    protected function setPageData(){       
        
        $this->data = [
            'type' => 'single_post',
            'infinite' => 'true'
        ];        
    }
 
    /**
     * Add the post info to data
     */
    protected function setPost() {        
        $this->data['post'] = $this->postTemplate->getPostData($this->post);
    }
 
    /**
     * set the list of the components
     * override this to add or remove components    
     */
    protected function initComponents() {
 
        $this->components = [
            'Author',
            'Taxonomies',
            'AnalysisTracking',
            'BreadcrumbsPrimary',
            'Adv',
            'Comments',                    
            'Relateds',
            'StructuredData',
            'Credits',
            'Canonical' ,
            'Meta'           
/data/websites/webnews/web/app/themes/webnews/src/ViewModel/Single.php
 */
class Single extends Base {
 
    protected $post;
    protected $postTemplate;
    
    protected $components;
    
    function __construct($post, $postTemplate = 'Single') {     
        
        parent::__construct();
 
        $this->post = $post;
 
        // assign the post template (default is 'Single')        
        $this->postTemplate = $this->postTemplateFactory($postTemplate);
        
        // init the post template 
        // (includes content details as well)
        $this->setPost(); 
 
        // set the components list into property
        // $components 
        $this->initComponents();
 
        // add all the default components 
        $this->setComponents();
 
    }
 
        
    /**
     * Writes the basic page info
     */
    protected function setPageData(){       
        
        $this->data = [
            'type' => 'single_post',
            'infinite' => 'true'
        ];        
/data/websites/webnews/web/app/themes/webnews/single.php
<?php
 
use Timber\Timber;
use THEME\ViewModel\Single;
use THEME\ViewModel\SingleMaxReina;
 
if (have_posts()) {
    
    $ajax = ( isset( $_GET['is_ajax_request']) );
 
    /// ottimizzazione per autore secondario di MaxReina
    $secondaryMaxReinaUser = get_user_by('login', WN_SECONDARY_USER_FOR_MAXREINA);
    $singleClass = $posts[0]->post_author == $secondaryMaxReinaUser->ID ? SingleMaxReina::class : Single::class;
    
    // load data
    $data = ( new $singleClass( $posts[0], 'SingleSplittedContent') )->getData();
 
    $template = $ajax ? 'singleAjax.twig' : 'single-notizia.twig';
    
    Timber::render( $template, $data);
}
 
?>
/data/websites/webnews/web/wp/wp-includes/template-loader.php
            }
 
            break;
        }
    }
 
    if ( ! $template ) {
        $template = get_index_template();
    }
 
    /**
     * Filters the path of the current template before including it.
     *
     * @since 3.0.0
     *
     * @param string $template The path of the template to include.
     */
    $template = apply_filters( 'template_include', $template );
    if ( $template ) {
        include $template;
    } elseif ( current_user_can( 'switch_themes' ) ) {
        $theme = wp_get_theme();
        if ( $theme->errors() ) {
            wp_die( $theme->errors() );
        }
    }
    return;
}
 
/data/websites/webnews/web/wp/wp-blog-header.php
<?php
/**
 * Loads the WordPress environment and template.
 *
 * @package WordPress
 */
 
if ( ! isset( $wp_did_header ) ) {
 
    $wp_did_header = true;
 
    // Load the WordPress library.
    require_once __DIR__ . '/wp-load.php';
 
    // Set up the WordPress query.
    wp();
 
    // Load the theme template.
    require_once ABSPATH . WPINC . '/template-loader.php';
 
}
 
/data/websites/webnews/web/index.php
<?php
/**
 * WordPress View Bootstrapper
 */
define('WP_USE_THEMES', true);
require __DIR__ . '/wp/wp-blog-header.php';
 

Environment & details:

empty
empty
empty
empty
empty
Key Value
SERVER_SOFTWARE nginx/1.20.2
REQUEST_URI /conto-gratis-con-revolut-tanti-vantaggi-e-bonus-di-10-euro-per-i-nuovi-clienti/
USER nginx
HOME /var/lib/nginx
HTTP_USER_AGENT Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
HTTP_ACCEPT */*
HTTP_CONNECTION close
HTTP_X_FORWARDED_FOR 3.144.193.129
HTTP_HOST staging.webnews.it
HTTP_X_FORWARDED_PORT 443
HTTP_X_FORWARDED_PROTO https
REDIRECT_STATUS 200
SERVER_NAME staging.webnews.it
SERVER_PORT 443
SERVER_ADDR 10.50.50.198
REMOTE_PORT 45776
REMOTE_ADDR 10.50.50.12
GATEWAY_INTERFACE CGI/1.1
HTTPS on
REQUEST_SCHEME https
SERVER_PROTOCOL HTTP/1.0
DOCUMENT_ROOT /data/websites/webnews/web
DOCUMENT_URI /index.php
SCRIPT_NAME /index.php
CONTENT_LENGTH
CONTENT_TYPE
REQUEST_METHOD GET
QUERY_STRING
SCRIPT_FILENAME /data/websites/webnews/web/index.php
FCGI_ROLE RESPONDER
PHP_SELF /index.php
REQUEST_TIME_FLOAT 1714948206.5253
REQUEST_TIME 1714948206
WP_ENV staging
WP_HOME https://staging.webnews.it
WP_SITEURL https://staging.webnews.it/wp
WP_DEBUG true
WP_CACHE false
WPLANG it_IT
WP_POST_REVISIONS false
DB_NAME webnews
DB_USER webnewsUSR
DB_PASSWORD M3l4.G4mes.N3ws
DB_HOST 10.50.50.198
GTM_ID_WEBNEWS GTM-P55CN7W
GTM_ID_MELABLOG GTM-P55CN7W
GTM_ID_GAMESBLOG GTM-P55CN7W
SITE_SPECIAL_TAXONOMY special
HTML_PRODUCT_POST_TYPE_SLUG prodotti
DISABLE_WP_CRON true
ACF_PRO_KEY b3JkZXJfaWQ9NzQ2MTV8dHlwZT1kZXZlbG9wZXJ8ZGF0ZT0yMDE2LTAyLTA5IDExOjQ5OjE5
WP_ALLOW_MULTISITE true
MULTISITE true
SUBDOMAIN_INSTALL true
DOMAIN_CURRENT_SITE staging.webnews.it
PATH_CURRENT_SITE /
SITE_ID_CURRENT_SITE 1
BLOG_ID_CURRENT_SITE 1
OFFERS_POST_TYPE post
OFFERS_LIMIT 2000
AUTH_KEY E094PtbN,/./X6J)N>X}iRr2X@qzrp<)*n!<1kps=WXH11Ho^1)^#,,/N`RQ%4PA
SECURE_AUTH_KEY rek_-EaoK.j{F>G%}^{Kw+npcA[tI&MW0_:3|Liq*E/]U/ve{M`tRW7[,SX-?G|b
LOGGED_IN_KEY Y;LL^L-^5a5nG&?I;e[nj0<5<;&pprgWk9Eq-Ozp>H6DZwl)3LUtdxQJ-b*mXA/w
NONCE_KEY 1R!zPZ.mPoo=[i1B[dUi2a13}0:>G{BToB:OX_(S8zT+PZ7nlEn78.#t0u7x?9)&
AUTH_SALT e+WE5olbqDvW7C[%Fs}d3n#_@8^,Ha<k&}kBL|:t@*Sl2vs#qT(lw`famVOPZ:!F
SECURE_AUTH_SALT Ks7ArUFaxK%!B`mqAsS;{qC,vpR36AiIcb@N1$[2<^SVY|?11$PV&P[PPzta,N<Z
LOGGED_IN_SALT 8lh7Q,HpTM}CMRtES%GVPD--09e!MUsQbinsA0-lS=qiUFfOaC]*,R?W$Po7UMak
NONCE_SALT m%tOV5u+uWm$e4V|4!y,:K_7`-N=m$unEx>SO:VZGMS)Y!h_ln/#zTJAkM)FK7{2
Key Value
WP_ENV staging
WP_HOME https://staging.webnews.it
WP_SITEURL https://staging.webnews.it/wp
WP_DEBUG true
WP_CACHE false
WPLANG it_IT
WP_POST_REVISIONS false
DB_NAME webnews
DB_USER webnewsUSR
DB_PASSWORD M3l4.G4mes.N3ws
DB_HOST 10.50.50.198
GTM_ID_WEBNEWS GTM-P55CN7W
GTM_ID_MELABLOG GTM-P55CN7W
GTM_ID_GAMESBLOG GTM-P55CN7W
SITE_SPECIAL_TAXONOMY special
HTML_PRODUCT_POST_TYPE_SLUG prodotti
DISABLE_WP_CRON true
ACF_PRO_KEY b3JkZXJfaWQ9NzQ2MTV8dHlwZT1kZXZlbG9wZXJ8ZGF0ZT0yMDE2LTAyLTA5IDExOjQ5OjE5
WP_ALLOW_MULTISITE true
MULTISITE true
SUBDOMAIN_INSTALL true
DOMAIN_CURRENT_SITE staging.webnews.it
PATH_CURRENT_SITE /
SITE_ID_CURRENT_SITE 1
BLOG_ID_CURRENT_SITE 1
OFFERS_POST_TYPE post
OFFERS_LIMIT 2000
AUTH_KEY E094PtbN,/./X6J)N>X}iRr2X@qzrp<)*n!<1kps=WXH11Ho^1)^#,,/N`RQ%4PA
SECURE_AUTH_KEY rek_-EaoK.j{F>G%}^{Kw+npcA[tI&MW0_:3|Liq*E/]U/ve{M`tRW7[,SX-?G|b
LOGGED_IN_KEY Y;LL^L-^5a5nG&?I;e[nj0<5<;&pprgWk9Eq-Ozp>H6DZwl)3LUtdxQJ-b*mXA/w
NONCE_KEY 1R!zPZ.mPoo=[i1B[dUi2a13}0:>G{BToB:OX_(S8zT+PZ7nlEn78.#t0u7x?9)&
AUTH_SALT e+WE5olbqDvW7C[%Fs}d3n#_@8^,Ha<k&}kBL|:t@*Sl2vs#qT(lw`famVOPZ:!F
SECURE_AUTH_SALT Ks7ArUFaxK%!B`mqAsS;{qC,vpR36AiIcb@N1$[2<^SVY|?11$PV&P[PPzta,N<Z
LOGGED_IN_SALT 8lh7Q,HpTM}CMRtES%GVPD--09e!MUsQbinsA0-lS=qiUFfOaC]*,R?W$Po7UMak
NONCE_SALT m%tOV5u+uWm$e4V|4!y,:K_7`-N=m$unEx>SO:VZGMS)Y!h_ln/#zTJAkM)FK7{2
0. Whoops\Handler\PrettyPageHandler