HEX
Server: Apache/2.4.25
System: Linux ion14 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64
User: (10087)
PHP: 7.4.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,system, exec, shell_exec, passthru, popen, proc_open
Upload Files
File: /home/www/web115/wordpress/wp-content/plugins/digimember/application/model/logic/access.php
<?php

class digimember_AccessLogic extends ncore_BaseLogic
{
    const max_access_denied_messages_per_page = 1;

    private static $current_product_id = false;

    private $CONTENT_FILTERS_TO_REMOVE_IF_ACCESS_IS_BLOCKED = array(

        // Thrive Visual Editor
        array(
            'filter'   => 'tve_editor_content',
            'priority' => array( 10, PHP_INT_MAX ),  // Thrive Visual Editor überschreibt jeglichen Content seiner Custom Posts / http://www.thrivethemes.com
        ),
    );

    private $CONTENT_FILTERS_TO_REMOVE_IF_ACCESS_IS_DELAYED = array(

        // Profit Builder
        array(
            'filter'   => array( 'pbuilder', 'replace_content' ),
            'priority' => 999,
        ),
    );

    private $POST_TYPES_NOT_TO_HANDLE_IN_THE_POSTS_FILTER = array(
        'coaching_cards' // for plugin Coaching Cards (http://successcoach.io)
    );

    private function onBlockedInsideTheLoop() {
        $GLOBALS['TVE_CONTENT_SKIP_ONCE'] = true; // für Thrive Visual Editor / http://www.thrivethemes.com
    }

    private $must_postcheck_content_access = false;

    public function setCurrentCourseProductId( $product_id )
    {
        $product_id = (int) $product_id;

        if (!$product_id) {
            return;
        }

        $must_update_settings = $product_id != self::$current_product_id;

        if ($must_update_settings) {
            /** @var ncore_UserSettingsData $userSettingsData */
            $userSettingsData = $this->api->load->model( 'data/user_settings' );
            $userSettingsData->set( 'cur_product_id', $product_id );
        }

        self::$current_product_id = $product_id;
    }

    public function resolveCurrentCourseProductId( $product_obj_or_id='current' )
    {
        $is_logged_in = ncore_userid()>0;
        if (!$is_logged_in) {
            return false;
        }

        $product_id = false;

        if (is_object($product_obj_or_id))
        {
            $product_id = ncore_retrieve( $product_obj_or_id, 'id' );
        }
        elseif (is_numeric( $product_obj_or_id ) && $product_obj_or_id > 0)
        {
            $product_id = $product_obj_or_id;
        }

        if (!$product_id)
        {
            $post = get_post();

            $post_id = ncore_retrieve( $post, array( 'ID', 'id' ) );

            if ($post_id) {
                /** @var digimember_PageProductData $pageProductData */
                $pageProductData = $this->api->load->model( 'data/page_product' );
                /** @var ncore_UserSettingsData $userSettingsData */
                $userSettingsData = $this->api->load->model( 'data/user_settings' );

                $product_id = ncore_retrieveGET( 'digimember_product_id', false );

                if (!$product_id) {
                    $product_id = $userSettingsData->get( 'cur_product_id' );
                }

                if ($product_id) {

                    $where = array( 'post_id' => $post_id, 'product_id' => $product_id, 'is_active' => 'Y' );

                    $is_valid = (bool) $pageProductData->getAll( $where );

                    if (!$is_valid) {
                        $product_id = false;
                    }
                }

                if (!$product_id)
                {
                    $where = array( 'post_id' => $post_id, 'is_active' => 'Y' );

                    $page_products = $pageProductData->getAll( $where );

                    switch (count($page_products))
                    {
                        case 0:
                            $product_id = false;
                            break;

                        case 1:
                            $product_id = $page_products[0]->product_id;
                            break;

                        default:
                            /** @var digimember_UserProductData $userProductData */
                            $userProductData = $this->api->load->model( 'data/user_product' );

                            $orders = $userProductData->getForUser();

                            foreach ( array_reverse( $orders ) as $order)
                            {
                                foreach ($page_products as $page_product)
                                {
                                    $is_match = $order->product_id = $page_product->product_id;
                                    if ($is_match) {
                                        $product_id = $order->product_id;
                                        break 2;
                                    }
                                }
                            }
                    }
                }
            }
        }

        $this->setCurrentCourseProductId( $product_id );

        return $product_id;
    }


    public function setupFilter()
    {
        add_filter( 'the_posts',                 array( $this, 'cbPostListFilter' ) );
        add_filter( 'wp_nav_menu_objects',       array( $this, 'cbPageListFilter' ) );
        add_filter( 'get_pages',                 array( $this, 'cbPageListFilter' ) );
        add_filter( 'wp',                        array( $this, 'cbRedirectingValidator' ) );
        add_filter( 'the_content',               array( $this, 'cbContentFilter' ), PHP_INT_MAX );
        // add_filter( 'tve_landing_page_content', array( $this, 'cbContentFilter' ), PHP_INT_MAX );

        add_filter( 'comments_open',             array( $this, 'cbBoolValidator' ) );
        add_filter( 'tcb_can_display_content',   array( $this, 'cbBoolValidator' ) );

        add_filter( 'widget_display_callback',   array( $this, 'cbDisplayWidgetsFilter' ), 10, 3 );


        add_filter( 'comments_template',         array( $this, 'cbCommentsTemplate' ), 1, 9999 );
        add_filter( 'comments_open',             array( $this, 'cbCommentsOpen' ), 2, 9999 );
        add_filter( 'get_comments_number',       array( $this, 'cbCommentsNumber' ), 2, 9999 );
        add_filter( 'pre_option_page_comments',  array( $this, 'cbPreOptionPagecomments' ), 1, 9999 );

        add_filter( 'template_include',          array( $this, 'cbTemplateInclude' ), PHP_INT_MAX, 1 );

        // add_filter( 'learn_press_get_courses',       array( $this, 'cbPostListFilter' ) );
        // add_filter( 'learn_press_course_object',     array( $this, 'cb_learn_press_course_object' ),  PHP_INT_MAX, 1 );
        // add_filter( 'learn_press_course_curriculum', array( $this, 'cb_learn_press_course_curriculum' ), PHP_INT_MAX, 3 );

        $this->registerCustomAccessFilters();
    }

    function cbTemplateInclude( $template_path )
    {
        if ($this->must_postcheck_content_access)
        {
            global $post;
            if ($post)
            {
                $result = $this->checkContentAccess( $post->post_type, $post->ID, $post_content='<dummy>', $force_full_access_denied_msg=true );

                $is_denied = $result && $result !== '<dummy>';

                if ($is_denied) {
                    global $DM_DENIED_MSG;
                    $DM_DENIED_MSG = $result;
                    return DIGIMEMBER_DIR . '/inc/forbidden.php';
                }
            }
        }

        return $template_path;
    }

    public function blockAccess( $user_id, $reason )
    {
        if (ncore_canAdmin( $user_id ))
        {
            $this->access_block_reason = '';
            return;
        }

        /** @var ncore_UserSettingsData $userSettingsData */
        $userSettingsData = $this->api->load->model( 'data/user_settings' );

        $date = ncore_dbDate( 'now', 'date' );

        $userSettingsData->setForUser( $user_id, 'access_limit_violated_at',     $date );
        $userSettingsData->setForUser( $user_id, 'access_limit_violated_reason', $reason );

        $this->access_block_reason = $reason;
    }

    public function unBlockAccess( $user_id )
    {

        /** @var ncore_UserSettingsData $userSettingsData */
        $userSettingsData = $this->api->load->model( 'data/user_settings' );
        $userSettingsData->setForUser( $user_id, 'access_limit_violated_at',     null );
        $userSettingsData->setForUser( $user_id, 'access_limit_violated_reason', null );

        $ipCounterModel = $this->api->load->model( 'data/ip_counter' );
        $ipCounterModel->deleteForUserId($user_id);
        /** @var digimember_BlogConfigLogic $blogConfig */
        $blogConfig = $this->api->load->model( 'logic/blog_config' );
        $blogConfig->set( 'limit_login_remove_email', '' );
    }

    public function blockAccessReason( $user_id='current' )
    {
        if ($this->access_block_reason!=='none')
        {
            return $this->access_block_reason;
        }

        if (!$user_id || $user_id==='current')
        {
            $user_id = ncore_userId();
        }

        if (ncore_canAdmin( $user_id ))
        {
            $this->access_block_reason = '';
            return '';
        }

        /** @var digimember_BlogConfigLogic $model */
        $model = $this->api->load->model( 'logic/blog_config' );
        $limit = $model->getIpAccessLimit();
        if (!$limit)
        {
            $this->access_block_reason = '';
            return '';
        }

        if (!$user_id)
        {
            $user_id = ncore_userId();
        }

        if (!$user_id)
        {
            return '';
        }

        /** @var ncore_UserSettingsData $userSettingsData */
        $userSettingsData = $this->api->load->model( 'data/user_settings' );

        $blocked_at = $userSettingsData->getForUser( $user_id, 'access_limit_violated_at' );

        if (!$blocked_at)
        {
            $this->access_block_reason = '';
            return '';
        }

        $date = ncore_dbDate( 'now', 'date' );
        if ($date != $blocked_at)
        {
            $this->access_block_reason = '';
            return '';
        }

        $this->access_block_reason = $userSettingsData->getForUser( $user_id, 'access_limit_violated_reason', 'unknown_reason' );

        return $this->access_block_reason;
    }

    public function loginUrl( $user_obj_or_id='current' )
    {
        if ($user_obj_or_id==='current') {
            $user_obj_or_id = ncore_userId();
        }

        $user_id = is_numeric( $user_obj_or_id )
                   ? $user_obj_or_id
                   : ncore_retrieve( $user_obj_or_id, array( 'ID', 'id' ) );
        if (!$user_id)
        {
            return false;
        }

        $product_model = $this->api->load->model( 'data/product' );
        $userpro_model = $this->api->load->model( 'data/user_product' );
        $counter_model = $this->api->load->model( 'data/counter' );

        $last_login_time = $counter_model->countLogin( $user_id );

        $user_products = array_reverse( $userpro_model->getForUser( $user_id ) );

        $login_url = false;

        foreach ($user_products as $one)
        {
            $product_id = $one->product_id;

            $product = $product_model->getCached( $product_id );
            if (!$product)
            {
                continue;
            }

            if ($login_url===false)
            {
                $login_url = $product->login_url;
            }

            $is_first_login = $one->has_visited_login_page == 'N';
            if ($is_first_login)
            {
                $data = array( 'has_visited_login_page' => 'Y' );
                $userpro_model->update( $one, $data );

                if ($product->first_login_url) {
                    return ncore_resolveUrl( $product->first_login_url );
                }
            }
        }

        return ncore_resolveUrl( $login_url );
    }


     private $removed_filters = array();

    private function disable3rdPartyContentFilters()
    {
        $third_party_content_filters = array(
            array(
                'function' => 'tve_clean_wp_editor_content',
                'priority' => -100,
            ),
            array(
                'class'        => 'SiteOrigin_Panels',
                'method'       => 'generate_post_content',
                'get_instance' => 'SiteOrigin_Panels::single',
                'priority'     => 10,
            ),
        );

        $filter_stack = array();

        foreach ($third_party_content_filters as $meta)
        {
            $callable = false;

            $have_function = !empty( $meta[ 'function' ] ) && function_exists( $meta[ 'function' ] );
            if ($have_function)
            {
                $callable = $meta[ 'function' ];
            }

            $have_class = !empty( $meta[ 'class' ] )
                       && !empty( $meta[ 'get_instance' ] )
                       && class_exists( $meta[ 'class' ] )
                       && is_callable( $meta[ 'get_instance' ] );
            if ($have_class)
            {
                $object = call_user_func(  $meta[ 'get_instance' ] );

                $callable = array( $object, $meta[ 'method' ] );
            }

            if (!$callable) {
                continue;
            }

            $priority = isset( $meta[ 'priority' ] )
                      ? $meta[ 'priority' ]
                      : 10;

            $is_removed = remove_filter( 'the_content', $callable, $priority );
            if ($is_removed) {
                $filter_stack[] = array( $callable, $priority );
            }
        }

        $this->removed_filters[] = $filter_stack;
    }

    private function enable3rdPartyContentFilters()
    {
        $filter_stack = array_pop( $this->removed_filters );
        foreach ($filter_stack as $function_prio)
        {
            list( $callable, $prio ) = $function_prio;
            add_filter( 'the_content', $callable, $prio );
        }
    }


    private function areCommentsVisible()
    {
        $post = get_post();

        if (!$post) {
            return true;
        }

        static $cache;

        $are_comments_visible =& $cache[ $post->ID ];
        if (isset($are_comments_visible)) {
            return $are_comments_visible;
        }

        list( $access_type, $wait_days, $product_id ) = $this->accessTypeComments( $post->post_type, $post );

        $must_check_comments = $access_type !== DIGI_ACCESS_FULL && $product_id!==false;
        if (!$must_check_comments) {
            $are_comments_visible = true;
            return $are_comments_visible;
        }

        $model = $this->api->load->model( 'data/product' );
        $product = $model->get( $product_id );

        $are_comments_visible = !$product
                             || $product->are_comments_protected == 'N';

        return $are_comments_visible;
    }

    public function cbCommentsTemplate( $template )
    {
        return $this->areCommentsVisible()
               ? $template
               : dirname(dirname(dirname(dirname( __FILE__ )))) . DIRECTORY_SEPARATOR . 'inc' . DIRECTORY_SEPARATOR . 'comments.php';
    }

    public function cbPreOptionPagecomments( $enabled )
    {
        return $this->areCommentsVisible()
               ? $enabled
               : 0;
    }
    public function cbCommentsNumber( $count, $post_id )
    {
        return $this->areCommentsVisible()
               ? $count
               : 0;
    }
    public function cbCommentsOpen( $open, $post_id )
    {
        return $this->areCommentsVisible()
               ? $open
               : false;
    }

    public function cbDisplayWidgetsFilter( $instance, $wp_widgets_obj, $args )
    {
        $is_digimember_widget = $wp_widgets_obj && is_a( $wp_widgets_obj, 'ncore_WidgetClass' );
        if (!$is_digimember_widget) {
            return $instance;
        }

        $visible_for                  = ncore_retrieve( $instance, 'dm_visible' );
        $required_owned_product_ids   = ncore_retrieve( $instance, 'dm_owned_product_ids' );
        $page_of_product_ids          = ncore_retrieve( $instance, 'dm_pages_of_product_ids' );

        $is_logged_in = ncore_userId() > 0;

        switch ($visible_for) {
            case 'logged_out':
                if ($is_logged_in) {
                    return false;
                }
                break;
            case 'logged_in':
                if (!$is_logged_in) {
                    return false;
                }
                break;
        }

        if ($required_owned_product_ids) {

            if (!$is_logged_in) {
                return false;
            }

            static $my_product_ids;
            if (!isset($my_product_ids))
            {
                /** @var digimember_UserProductData $model */
                $model = $this->api->load->model( 'data/user_product' );
                $user_products = $model->getForUser( 'current' );

                $this->api->load->helper( 'array' );
                $my_product_ids = ncore_retrieveValues( $user_products, 'product_id' );
            }

            if (!$my_product_ids) {
                return false;
            }

            $required_owned_product_ids = explode( ',', $required_owned_product_ids );

            $owns_product = in_array( 'all', $required_owned_product_ids );
            if (!$owns_product) {
                $owns_product = (bool) array_intersect( $required_owned_product_ids, $my_product_ids );
            }

            if (!$owns_product) {
                return false;
            }
        }

        if ($page_of_product_ids)
        {
            if (!$is_logged_in) {
                return false;
            }

            $page = get_post();

            if (!isset($page->post_type) || $page->post_type != 'page') {
                return false;
            }

            static $page_products;
            if (!isset($page_products)) {
                /** @var digimember_PageProductData $model */
                $model = $this->api->load->model( 'data/page_product' );
                $page_products = $model->getForPage( $page->post_type, $page->ID, true );
            }

            if (!$page_products) {
                return false;
            }

            $page_of_product_ids = explode( ',', $page_of_product_ids );
            $is_page_of_product = in_array( 'all', $page_of_product_ids );

            if (!$is_page_of_product) {
                foreach ($page_products as $one)
                {
                    if (in_array( $one->product_id, $page_of_product_ids )) {
                        $is_page_of_product = true;
                        break;
                    }
                }
            }

            if (!$is_page_of_product) {
                return false;
            }
        }

        return $instance;
    }

    public function cbPostListFilter( $posts )
    {
        if (!$posts)
        {
            return array();
        }

        $allowed_posts = $this->filterList( $posts, $allowAllPages=true );

        $must_handle_denied_access = count( $allowed_posts ) == 0
                                  && !ncore_isInSidebar();
        if ($must_handle_denied_access)
        {
            if (empty($posts)) {
                return array();
            }



            $post = $posts[0];

            $post_id   =  ncore_retrieve( $post, 'ID' );
            $post_type =  ncore_retrieve( $post, 'post_type' );
            $content   =  ncore_retrieve( $post, 'post_content' );

            $is_handled = !in_array( $post_type, $this->POST_TYPES_NOT_TO_HANDLE_IN_THE_POSTS_FILTER );

            if ($is_handled) {

                $this->checkRedirectAccess($post_type, $post_id);

                $post->post_content = $this->checkContentAccess($post_type, $post_id, $content);

                if (empty($post->post_excerpt)) {
                    $theme = wp_get_theme();
                    if ($theme->name != 'Twenty Twenty') {
                        $post->post_excerpt = apply_filters('get_the_excerpt', $content);
                    }
                }

                $this->clear3rdPartyContentFiltersForBlockedContent();

                return array($post);
            }
        }

        return $allowed_posts;
    }

    public function cbPageListFilter( $pages )
    {
        return $this->filterList( $pages, $allowAllPages=false);
    }

    private function filterList( $pages, $allowAllPages )
    {
        $readablePages = array();

        foreach ($pages as $page) {

            $post_type = $page->post_type;

            if ($allowAllPages && $post_type=='page')
            {
                $readablePages[] = $page;
                continue;
            }

            list( $access_type, $wait_days, $product_id ) = $this->accessType( $post_type, $page );

            $access_granted = $access_type != DIGI_ACCESS_NONE;

            if ($access_granted)
            {
                $readablePages[] = $page;
            }
        }

        return $readablePages;
    }

/*
    public function cb_learn_press_course_object( $course )
    {
        list( $access_type, $wait_days, $product_id ) = $this->accessType( "lp_course", $course );

        $has_access = $access_type === DIGI_ACCESS_FULL;
        if ($has_access) {
            return $course;
        }

        $course->post_content = $this->checkContentAccess( $course->post_type, $course->ID, $course->post_type, $force_full_access_denied_msg=true );

        return $course;
    }

    public function cb_learn_press_course_curriculum( $curriculum, $course_id, $secion_id )
    {
        list( $access_type, $wait_days, $product_id ) = $this->accessType( "lp_course", $course_id );

        $has_access = $access_type === DIGI_ACCESS_FULL;
        if ($has_access) {
            return $curriculum;
        }

        return  false;
    }
*/

    public function cbContentFilter( $content )
    {
        if (!$this->isAccessCheckEnabled())
        {
            return $content;
        }

        list( $post_id, $post_type ) = ncore_currentPostIdAndType();

        if ($post_id) {
            return $this->checkContentAccess( $post_type, $post_id, $content );
        }
        else
        {
            $this->must_postcheck_content_access = true;

            // I do not rember what theme/plugin requires this filter to return '' for $post_id == 0.
            //
            // I have tested:
            // ( -   = does not have $post_id 0
            //   c   = has $post_id 0, but expects $contents to be returned
            //   !!! = has $post_id 0, but expects '' to be returned )
            //
            // BuddyPress:      c
            // OptimizePress 2: -
            // ProfitBuilder:   -
            //
            // Christian / 08/26/2015

            return $content; //   '';
        }
    }

    public function cbRedirectingValidator()
    {
        list( $post_id, $post_type ) = ncore_currentPostIdAndType();

        $this->checkRedirectAccess( $post_type, $post_id );
    }

    public function cbBoolValidator( $access_allowed=true )
    {
        if (!$access_allowed)
        {
            return false;
        }

        list( $post_id, $post_type ) = ncore_currentPostIdAndType();

        list( $access_type, $wait_days, $product_id ) = $this->accessType( $post_type, $post_id );

        return $access_type == DIGI_ACCESS_FULL;
    }

    static private $access_cache = array();
    private $access_block_reason = 'none';

    public function accessType( $post_type='current', $obj_or_id='current', $user_id='current' )
    {
        return apply_filters('digimember/access/accessType', $this->_accessType($post_type, $obj_or_id, $user_id), $post_type, $obj_or_id, $user_id);
    }

    /**
     * Seperate accesscheck for the comments section to prevent access overwrite on elementor posts
     * implemented for issue/DM-237
     * @param string $post_type
     * @param string $obj_or_id
     * @param string $user_id
     * @return array|mixed
     */
    public function accessTypeComments( $post_type='current', $obj_or_id='current', $user_id='current' )
    {
        return $this->_accessType($post_type, $obj_or_id, $user_id);
    }

    public function _accessType( $post_type='current', $obj_or_id='current', $user_id='current' )
    {
        if ($user_id==='current')
        {
            $user_id = ncore_userId();
        }

        $is_blocked = (bool) $this->blockAccessReason( $user_id );
        if ($is_blocked)
        {
            return array( DIGI_ACCESS_NONE, $wait_days = false, $product_id=false, $must_ask=false, $unlock_datetime=false );
        }

        list( $post_type, $post_id ) = $this->_resolvePostType( $post_type, $obj_or_id );
        if (!$post_id)
        {
            return array( DIGI_ACCESS_FULL, $wait_days = false, $product_id=false, $must_ask=false, $unlock_datetime=false );
        }

        $blog_id = ncore_blogId();

        $access_info =& self::$access_cache[ $blog_id ][ $user_id ][ $post_type ][ $post_id ];

        if (isset($access_info))
        {
            return $access_info;
        }

        /** @var digimember_PageProductData $page_product_model */
        $page_product_model = $this->api->load->model( 'data/page_product' );

        $full_access = array( DIGI_ACCESS_FULL, $wait_days = false, $product_id=false, $must_ask=false, $unlock_datetime=false );

        $type_is_handled_by_me = $page_product_model->isPostTypeHandled( $post_type );
        if (!$type_is_handled_by_me)
        {
            return $access_info=$full_access;
        }

        if (current_user_can('manage_options') )
        {
            return $access_info=$full_access;
        }

        /** @var digimember_UserProductData $user_product_model */
        $user_product_model = $this->api->load->model( 'data/user_product' );

        $postProducts = $page_product_model->getForPage( $post_type, $post_id, $active_only=true );

        if (!$postProducts)
        {
            return $access_info = $full_access;
        }

        $userProducts = $user_product_model->getForUser( $user_id );

        $is_in_menu      = false;
        $wait_days       = false;
        $product_id      = false;
        $unlock_datetime = false;

        $preview_product_id=false;

        $access_with_waiver = false;

        $product_accessinfo_mapping = array();
        foreach ($postProducts as $postProduct)
        {
            if (!$product_id)
            {
                $product_id = $postProduct->product_id;
            }

            $have_match = false;

            foreach ($userProducts as $userProduct)
            {
                $matches = $postProduct->product_id == $userProduct->product_id;
                if (!$matches)
                {
                    continue;
                }

                $have_match = true;

                list( $one_is_readable, $one_is_in_menu, $one_wait_days, $one_has_preview, $one_ask_for_waiver, $one_unlock_datetime, $one_has_error_handling_priority ) = $this->_checkAccess( $postProduct, $userProduct );

                $product_accessinfo_mapping[] = array(
                    'product_id' => $postProduct->product_id,
                    'is_readable' => $one_is_readable,
                    'is_in_menu' => $one_is_in_menu,
                    'wait_days' => $one_wait_days,
                    'has_preview' => $one_has_preview,
                    'ask_for_waiver' => $one_ask_for_waiver,
                    'unlock_datetime' => $one_unlock_datetime,
                    'has_error_handling_priority' => $one_has_error_handling_priority
                );
                if ($one_is_readable==='skip')
                {
                    continue;
                }

                $product_id = $postProduct->product_id;

                if ($one_is_readable)
                {
                    if ($one_ask_for_waiver) {
                        $product_accessinfo_mapping[array_key_last($product_accessinfo_mapping)]['ask_for_waiver'] = array( DIGI_ACCESS_WAIVER, $one_wait_days, $product_id );
                        continue;
                    }
                    else
                    {
                        return $access_info = $full_access;
                    }
                }

                if ($wait_days === false)
                {
                    $wait_days       = $one_wait_days;
                    $product_accessinfo_mapping[array_key_last($product_accessinfo_mapping)]['unlock_datetime'] = $one_unlock_datetime;
                }
                elseif ($one_wait_days !== false && $one_wait_days < $wait_days)
                {
                    $wait_days       = $one_wait_days;
                    $product_accessinfo_mapping[array_key_last($product_accessinfo_mapping)]['unlock_datetime'] = $one_unlock_datetime;
                }
            }

            if (!$have_match)
            {
                list( $one_is_readable, $one_is_in_menu, $one_wait_days, $one_has_preview, $one_ask_for_waiver, $one_unlock_datetime, $one_has_error_handling_priority ) = $this->_checkAccess( $postProduct, $userProduct=false );
                $product_accessinfo_mapping[] = array(
                    'product_id' => $postProduct->product_id,
                    'is_readable' => $one_is_readable,
                    'is_in_menu' => $one_is_in_menu,
                    'wait_days' => $one_wait_days,
                    'has_preview' => $one_has_preview,
                    'ask_for_waiver' => $one_ask_for_waiver,
                    'unlock_datetime' => $one_unlock_datetime,
                    'has_error_handling_priority' => $one_has_error_handling_priority
                );

                if ($one_is_readable === 'skip')
                {
                    continue;
                }

                if ($one_is_readable)
                {
                    return $access_info = $full_access;
                }

                $product_id = $postProduct->product_id;

                if ($one_has_preview)
                {
                    $preview_product_id = $product_id;
                }

                if ($one_is_in_menu)
                {
                    $is_in_menu = true;
                }
            }
        }

        list( $product_id, $is_in_menu, $wait_days, $preview_product_id, $access_with_waiver, $unlock_datetime) = $this->getPriorizedAccessinfo($product_accessinfo_mapping);

        if ($access_with_waiver) {
            return $access_with_waiver;
        }

//        if (!$this->mayShowInMenu( $post_type ))
//        {
//            $is_in_menu = false;
//        }

//        $access_type = $is_in_menu
//                     ? DIGI_ACCESS_MENU
//                     : ($preview_product_id
//                        ? DIGI_ACCESS_PREV
//                        : DIGI_ACCESS_NONE);
//
//        if ($access_type==DIGI_ACCESS_PREV)
//        {
//            $product_id = $preview_product_id;
//        }

        if ($is_in_menu && $preview_product_id) {
            $access_type = DIGI_ACCESS_PREV;
        }
        else {
            $access_type = $is_in_menu
                ? DIGI_ACCESS_MENU
                : ($preview_product_id
                    ? DIGI_ACCESS_PREV
                    : DIGI_ACCESS_NONE);
        }

        if ($access_type==DIGI_ACCESS_PREV)
        {
            $product_id = $preview_product_id;
        }

        return $access_info = array( $access_type, $wait_days, $product_id, $unlock_datetime );
    }

    private function getPriorizedAccessinfo($accessInfoMapping) {
        $atLastOneInMenu = false;
        foreach ($accessInfoMapping as $accessInfo) {
            if ($accessInfo['is_in_menu']) {
                $atLastOneInMenu = true;
            }
        }
        foreach ($accessInfoMapping as $accessInfo) {
            if ($accessInfo['has_preview']) {
                $preview_id = $accessInfo['product_id'];
            }
            else {
                $preview_id = false;
            }
            if ($accessInfo['is_readable']) {
                if ($atLastOneInMenu) {
                    $accessInfo['is_in_menu'] = true;
                }
                return array($accessInfo['product_id'], $accessInfo['is_in_menu'], $accessInfo['wait_days'], $preview_id, $accessInfo['ask_for_waiver'], $accessInfo['unlock_datetime'] );
            }
        }
        foreach ($accessInfoMapping as $accessInfo) {
            if ($accessInfo['has_preview']) {
                $preview_id = $accessInfo['product_id'];
            }
            else {
                $preview_id = false;
            }
            if ($accessInfo['wait_days'] && $accessInfo['wait_days'] > 0) {
                if ($atLastOneInMenu) {
                    $accessInfo['is_in_menu'] = true;
                }
                return array($accessInfo['product_id'], $accessInfo['is_in_menu'], $accessInfo['wait_days'], $preview_id, $accessInfo['ask_for_waiver'], $accessInfo['unlock_datetime'] );
            }
        }
        foreach ($accessInfoMapping as $accessInfo) {
            if ($accessInfo['has_preview']) {
                $preview_id = $accessInfo['product_id'];
            }
            else {
                $preview_id = false;
            }
            if ($accessInfo['has_error_handling_priority']) {
                if ($atLastOneInMenu) {
                    $accessInfo['is_in_menu'] = true;
                }
                return array($accessInfo['product_id'], $accessInfo['is_in_menu'], $accessInfo['wait_days'], $preview_id, $accessInfo['ask_for_waiver'], $accessInfo['unlock_datetime'] );
            }
        }
        $firstProductMapping = $accessInfoMapping[0];
        if ($firstProductMapping['has_preview']) {
            $preview_id = $firstProductMapping['product_id'];
        }
        else {
            $preview_id = false;
        }
        if ($atLastOneInMenu) {
            $firstProductMapping['is_in_menu'] = true;
        }
        return array($firstProductMapping['product_id'], $firstProductMapping['is_in_menu'], $firstProductMapping['wait_days'], $preview_id, $firstProductMapping['ask_for_waiver'], $firstProductMapping['unlock_datetime'] );
    }


    private function _resolvePostType( $post_type='current', $obj_or_id='current' )
    {
        if ($obj_or_id === 'current')
        {
            $post = get_post();
            if ($post) {
                $obj_or_id = $post;
                $post_type = $post->post_type;
            }
            else
            {
                $obj_or_id = false;
                $post_type = 'page';
            }
        }

        if ($post_type === 'current')
        {
            $post = get_post( $obj_or_id );
            $post_type = $post
                       ? $post->post_type
                       : 'page';
        }

        $is_id = is_numeric( $obj_or_id );
        if ($is_id)
        {
            $post_id = $obj_or_id;
            return array( $post_type, $post_id );
        }

        $obj = $obj_or_id;

        $model = $this->api->load->model( 'data/page_product' );
        $type_valid = in_array( $post_type, $model->postTypes() );

        if ($type_valid)
        {
            $post_id = ncore_retrieve( $obj, 'ID' );
            return array( $post_type, $post_id );
        }

        $post_id   = ncore_retrieve( $obj, 'object_id' );
        $post_type = ncore_retrieve( $obj, 'object' );

        return array( $post_type, $post_id );
    }


    private function _checkAccess( $postProduct, $userProduct )
    {
        $product_id = $postProduct->product_id;

        /** @var digimember_ProductData $productData */
        $productData = $this->api->load->model( 'data/product' );

        $product = $productData->getCached( $product_id );

        if (!$product)
        {
            return array( $is_readable='skip', $is_in_menu=false, $wait_days=false, $has_preview=false, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority=false );
        }

        if (!$product->published)
        {
            return array( $is_readable=false, $is_in_menu=false, $wait_days=false, $has_preview=false, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority=false );
        }

        /** @var digimember_HasPreviewCacheData $hasPreviewCacheData */
        $hasPreviewCacheData = $this->api->load->model( 'data/has_preview_cache' );
        $has_preview = $hasPreviewCacheData->hasPreview( $postProduct->post_id );

        $is_logged_in = ncore_isLoggedIn();
        $has_error_handling_priority = $product->access_denied_priority == 'Y' ? true : false;
        if (!$is_logged_in)
        {
            $is_in_menu = $product->show_in_menu_if_logged_out;
            return array( $is_readable=false, $is_in_menu, $wait_days=false, $has_preview, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority );
        }

        if (!$userProduct || $userProduct->is_active=='N')
        {
            $is_in_menu = $product->show_in_menu_if_not_bought;
            return array( $is_readable=false, $is_in_menu, $wait_days=false, $has_preview, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority );
        }

        if ($userProduct->is_access_too_early)
        {
            return array( $is_readable='skip', $is_in_menu=false, $wait_days=false, $has_preview=false, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority );
        }

        if ($userProduct->is_access_too_late)
        {
            return array( $is_readable='skip', $is_in_menu=false, $wait_days=false, $has_preview=false, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority );
        }

        if ($userProduct->is_access_expired)
        {
            return array( $is_readable=false, $is_in_menu=false, $wait_days=false, $has_preview, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority );
        }

        $is_unlocked = $this->_matchesUnlockPolicy( $product, $postProduct, $userProduct );
        if (!$is_unlocked) {
            return array( $is_readable='skip', $is_in_menu=false, $wait_days=false, $has_preview=false, $ask_for_waiver=false, $unlock_datetime=false, $has_error_handling_priority );
        }

        $must_ask_for_waiver = ncore_isTrue( $product->is_right_of_withdrawal_waiver_required )
                            && ncore_isFalse( $userProduct->is_right_of_rescission_waived );

        if (!$postProduct->unlock_day)
        {
            $age_in_days = $userProduct->age_in_days;
            $unlock_datetime = false;
            $unlock_day = 0;
        }
        else
        {
            switch ($product->unlock_mode)
            {
                case 'fix_date':

                    $this->api->load->helper( 'date' );
                    static $now_unix, $now_hour, $now_date;
                    if (!isset($now_unix))
                    {
                        $now_unix = time() +  3600 * get_option('gmt_offset');
                        $now_hour = date( 'H:i:s', $now_unix );
                        $now_date = ncore_dbDate( $now_unix, 'date' );
                    }

                    $product_unlock_datetime = $product->unlock_start_date;
                    list( $product_unlock_date, $product_unlock_time ) = ncore_retrieveList( ' ', $product->unlock_start_date );
                    $product_content_unlock_datetime = ncore_dateAddDays($product_unlock_date, $postProduct->unlock_day).' '.$product_unlock_time;

                    if (($diffInSeconds = ncore_unixDiffSeconds(ncore_unixDate($product_unlock_datetime), ncore_unixDate($product_content_unlock_datetime))) > 0) {
                        $daysleft = ncore_dateDiffDaysCeil($product_content_unlock_datetime, 'now', false);
                        $unlock_day = $daysleft;
                        $unlock_datetime = $product_content_unlock_datetime;
                        $age_in_days = 0;
                    }
                    else {
                        $unlock_day = 0;
                        $unlock_datetime = false;
                        $age_in_days = ncore_dateDiffDays( $product_unlock_date, $now_date, false );
                    }
                    break;
                case 'order_date':
                default:
                    $unlock_day  = $postProduct->unlock_day;
                    $age_in_days = $userProduct->age_in_days;
                    $unlock_datetime = false;


            }
        }

        $is_readable_all_the_time = $unlock_day <= 0;
        if ($is_readable_all_the_time)
        {
            return array( $is_readable=true, $is_in_menu=true, $wait_days=false, $has_preview=false, $must_ask_for_waiver, $unlock_datetime=false, $has_error_handling_priority );
        }

        $days_left = $unlock_day - $age_in_days;

        $is_readable_now = $days_left <= 0 || !$this->haveWaitDays();

        if ($is_readable_now)
        {
            return array( $is_readable=true, $is_in_menu=true, $wait_days=false, $has_preview=false, $must_ask_for_waiver, $unlock_datetime=false, $has_error_handling_priority );
        }

        $is_in_menu = $product->show_in_menu_if_locked;
        return array( $is_readable=false, $is_in_menu, $days_left, $has_preview, $ask_for_waiver=false, $unlock_datetime, $has_error_handling_priority );
    }


    private function mayShowInMenu( $post_type )
    {
        return $post_type != 'post';
    }


    private function checkContentAccess( $post_type, $post_id, $content, $force_full_access_denied_msg=false )
    {
        $is_this_an_excerpt_call = !in_the_loop() && !$content;
        if ($is_this_an_excerpt_call) {
            return '';
        }

        list( $access_type, $wait_days, $product_id, $unlock_datetime ) = $this->accessType( $post_type, $post_id );

        if ($access_type == DIGI_ACCESS_FULL)
        {
            return $content;
        }

        if ($access_type == DIGI_ACCESS_WAIVER)
        {
            $this->_handleAccessWaiver( $product_id );
            if (!$wait_days)
            {
                return $content;
            }
        }

        if ($access_type == DIGI_ACCESS_PREV)
        {
            $controller = $this->api->load->controller( 'shortcode' );
            $shortcode = $controller->shortCode( 'preview' );

            list( $preview, $protected ) = ncore_retrieveList( "<!-- $shortcode -->", $content, 2, true );

            $this->_trimHtmlCommentParts( $preview );

            if ($protected)
            {
                if ($this->isContentShownEvenIfPageLocked($content)) {
                    return $content;
                }

                if ($this->isLockedHintHidden( $content ))
                {
                    return '';
                }

                list( $message, $css, $redirect_url ) = $wait_days > 0
                         ? $this->renderWaitDaysMessage( $product_id, $wait_days, $unlock_datetime )
                         : $this->renderSalesLetter( $product_id );

                if ($redirect_url) {
                    ncore_redirect( $redirect_url );
                }

                return "$preview<div class='$css'>$message</div>";
            }

            return $content;
        }

        if ($this->isContentShownEvenIfPageLocked($content)) {
            return $content;
        }
        elseif ($wait_days === false)
        {
            $show_full_access_denied_msg = in_the_loop() || $force_full_access_denied_msg;

            if ($show_full_access_denied_msg)
            {
                $this->onBlockedInsideTheLoop();

                static $access_denied_msg_rendered;

                if (empty($access_denied_msg_rendered)) {
                    $access_denied_msg_rendered = 1;
                } else {
                    $access_denied_msg_rendered++;
                }

                if ($access_denied_msg_rendered>self::max_access_denied_messages_per_page) {
                    return '';
                }
            }

            $is_logged_in = 0&&ncore_isLoggedIn(); //why? discover later
            if ($is_logged_in || !$show_full_access_denied_msg)
            {
                $css = 'digimeber_content_locked';
                $message = _digi( 'Sorry! You don\'t have access to this page.' );
            }
            else
            {
                if ($product_id) {
                    $model = $this->api->load->model( 'data/product' );
                    $product = $model->get( $product_id );

                    $show_text = $product->access_denied_type == DIGIMEMBER_AD_TEXT
                              && $product->access_denied_text;

                    if ($show_text)
                    {
                        return do_shortcode( $product->access_denied_text );
                    }
                }

                $settings = array( 'redirect_url' => ncore_currentUrl() );
                $controller = $this->api->load->controller( 'user/login_form', $settings );

                ob_start();
                $controller->dispatch();
                return ob_get_clean();
            }
        }
        elseif ($this->isLockedHintHidden( $content ))
        {
            return '';
        }
        else
        {
            list( $message, $css, $redirect_url ) = $this->renderWaitDaysMessage( $product_id, $wait_days, $unlock_datetime, $post_id );

            if ($redirect_url) {
                ncore_redirect( $redirect_url );
            }
        }

        return "<p class='digimember_access_denied $css'>$message</p>";
    }


    private function checkRedirectAccess( $post_type, $post_id )
    {
        list( $access_type, $wait_days, $product_id, $unlock_datetime ) = $this->accessType( $post_type, $post_id );

        $will_be_unlocked_automatically = $wait_days !== false;

        if ($access_type == DIGI_ACCESS_WAIVER)
        {
            $this->_handleAccessWaiver( $product_id );
        }

        $must_redirect = $access_type != DIGI_ACCESS_FULL
                      && $access_type != DIGI_ACCESS_PREV
                      && !$will_be_unlocked_automatically;

        if (!$must_redirect)
        {
            return;
        }

        $this->api->load->model( 'data/product' );
        $product = $this->api->product_data->getCached( $product_id );
        if (!$product)
        {
            return;
        }

        switch ($product->access_denied_type)
        {
            case DIGIMEMBER_AD_URL:
                $url = $product->access_denied_url;
                break;

            case DIGIMEMBER_AD_PAGE:
                $page_id = $product->access_denied_page;

                $url = $page_id;

                $have_id = is_numeric( $page_id ) && $page_id > 0;
                if ($have_id)
                {
                    list( $access_type, $wait_days, $product_id ) = $this->accessType( 'page', $page_id );
                    if ($access_type != DIGI_ACCESS_FULL)
                    {
                        return;
                    }
                }
                break;

            case DIGIMEMBER_AD_LOGIN:
            CASE DIGIMEMBER_AD_TEXT:
            default:
                return;
        }

        if ($url)
        {
            $this->api->load->helper( 'url' );
            $url = ncore_resolveUrl( $url );
        }
        else
        {
            $url = ncore_siteUrl();
        }

        ncore_redirect( $url );
        exit;
    }


    private function isLockedHintHidden( $content )
    {
         if (ncore_isOptimizePressPage()) {
            $controller = $this->api->load->controller( 'shortcode' );
            $shortcode = $controller->shortcode('op_locked_hint');

            $shortcode = "<!-- $shortcode -->";

            $have_shortcode = strpos( $content, $shortcode ) !== false;

            return !$have_shortcode;
        }

        return false;
    }

    private function isContentShownEvenIfPageLocked( &$content )
    {
         if (ncore_isOptimizePressPage()) {
            $controller = $this->api->load->controller( 'shortcode' );
            $shortcode = $controller->shortcode('op_show_always');

            $shortcode = "<!-- $shortcode -->";

            $have_shortcode = strpos( $content, $shortcode ) !== false;

            if ($have_shortcode) {
                $this->_trimHtmlCommentParts( $content );
            }

            return $have_shortcode;
        }

        return false;
    }

    private function renderWaitDaysMessage( $product_id, $wait_days, $unlock_datetime=false, $post_id=null )
    {
        $model = $this->api->load->model( 'data/product' );
        $product = $model->get( $product_id );

        $message = '';
        $css     = '';
        $url     = '';

        $content_later_type = ncore_retrieve( $product, 'content_later_type', DIGIMEMBER_AD_TEXT );

        switch ($content_later_type)
        {
            case DIGIMEMBER_AL_PAGE:
                $url = ncore_resolveUrl( $product->content_later_page );
                break;

            case DIGIMEMBER_AL_URL:
                $url = ncore_resolveUrl( $product->content_later_url );
                break;

            case DIGIMEMBER_AD_TEXT:
            default:

                $this->api->load->helper( 'date' );

                if ($unlock_datetime)
                {
                    $time_unix    = time() + 3600 * get_option('gmt_offset');
                    $date         = ncore_formatDate( $unlock_datetime );
                    $wait_seconds = strtotime( $unlock_datetime ) - $time_unix;

                    list( $unlock_date, $time ) = ncore_retrieveList( ' ', $unlock_datetime, 2, true );
                    $is_today    = $unlock_date == ncore_dbDate( 'now', 'date' );
                    $is_tomorrow = $unlock_date == ncore_dbDate( $time_unix+86400, 'date' );

                    if (ncore_stringEndsWith( $time, ':00')) {
                        $time = substr( $time, 0, -3 );
                    }

                    if ($is_today)
                    {
                        $css = 'digimeber_wait_1_more_day';
                        $message = _digi( 'This content will be unlocked today at %s.', $time );
                    }
                    elseif ($is_tomorrow)
                    {
                        $css = 'digimeber_wait_1_more_day';
                        $message = _digi( 'This content will be unlocked today at %s.', $time );
                    }
                    else
                    {
                        $css = sprintf( 'digimeber_wait_%s_more_days', $wait_days );
                        $message = _digi( 'This content will be unlocked on %s at %s.', $date, $time );
                    }

                }
                else
                {
                    $wait_seconds = 86400*$wait_days;
                    $date         = ncore_formatDate( time() + $wait_seconds );
                    $time         = '0:00';

                    if ($wait_days == 1)
                    {
                        $css = 'digimeber_wait_1_more_day';
                        $message = _digi( 'This content will be unlocked tomorrow.' );
                    }
                    else
                    {
                        $css = sprintf( 'digimeber_wait_%s_more_days', $wait_days );
                        $message = _digi( 'This content will be unlocked in %s days.', $wait_days );
                    }
                }

                $content_later_msg = trim(ncore_retrieve( $product, 'content_later_msg' ));
                $have_custom_msg = $content_later_msg
                                && strlen( trim( strip_tags( $content_later_msg ) ) ) >= 3;
                if ($have_custom_msg) {

                    $in_days = ncore_formatTimeSpan( $wait_seconds, 'in' );

                    $find = array( '[DAYS]', '[DATE]', '[TIME]', '[IN_DAYS]' );
                    $repl = array( $wait_days, $date,  $time,    $in_days );

                    $this->clear3rdPartyContentFiltersForDelayedContent();

                    $this->disableAccessCheck();
                    $this->disable3rdPartyContentFilters();
                    if (ncore_isOptimizePressPageForId($post_id)) {
                        $message = str_replace( $find, $repl, $content_later_msg );
                    }
                    else {
                        $message = apply_filters( 'the_content', str_replace( $find, $repl, $content_later_msg ) );
                    }
                    $this->enable3rdPartyContentFilters();
                    $this->enableAccessCheck();
                }
        }



        return array( $message, $css, $url );
    }

    private static $access_check_disabled = 0;
    private function disableAccessCheck()
    {
        self::$access_check_disabled++;
    }
    private function enableAccessCheck()
    {
        self::$access_check_disabled--;
        if (self::$access_check_disabled < 0) {
            self::$access_check_disabled = 0;
        }
    }

    private function isAccessCheckEnabled()
    {
        return self::$access_check_disabled == 0;
    }

    private function renderSalesLetter(  $product_id )
    {
        $model = $this->api->load->model( 'data/product' );
        $product = $model->get( $product_id );

        $sales_letter = ncore_retrieve( $product, 'sales_letter' );

        $css = 'digimember_salesletter';

        $sales_letter = apply_filters( 'the_content', $sales_letter );

        return array( $sales_letter, $css, $url=false );
    }

    private static $have_wait_days = array();
    private function haveWaitDays()
    {
        $have_it =& self::$have_wait_days[ ncore_blogId() ];

        if (!isset( $have_it) )
        {
            $model = $this->api->load->model( 'logic/features' );
            $have_it = $model->canContentsBeUnlockedPeriodically();
        }
        return $have_it;
    }

    private function registerCustomAccessFilters()
    {
        /** @var digimember_CustomAccessFiltersLib $customAccessFilters */
        $customAccessFilters = $this->api->load->library('custom_access_filters');
        $customAccessFilters->registerFilters(function() {
            list($post_id) = ncore_currentPostIdAndType();
            return [$post_id, !$this->cbBoolValidator()];
        });
    }


    private function clear3rdPartyContentFiltersForBlockedContent()
    {
        $this->clear3rdPartyContentFilters( $this->CONTENT_FILTERS_TO_REMOVE_IF_ACCESS_IS_BLOCKED );
    }

    private function clear3rdPartyContentFiltersForDelayedContent()
    {
        $this->clear3rdPartyContentFilters( $this->CONTENT_FILTERS_TO_REMOVE_IF_ACCESS_IS_DELAYED );
    }


    private function clear3rdPartyContentFilters( $filters_to_remove )
    {
        static $are_filters_removed;

        if (!empty($are_filters_removed)) {
            return;
        }
        $are_filters_removed = true;

        foreach ($filters_to_remove as $rec)
        {
            $filter     = $rec[ 'filter' ];
            $priorities = $rec[ 'priority' ];

            $have_plain_function = is_string( $filter );
            $have_object         = is_array( $filter );

            if ($have_plain_function && !function_exists( $filter )) {
                continue;
            }
            elseif ($have_object)
            {
                list( $object_name, $method_name ) = $filter;

                if (empty($GLOBALS[ $object_name ])) {
                    continue;
                }

                $filter = array( $GLOBALS[ $object_name ], $method_name );
            }
            else {
                // configuration error
                continue;
            }

            if (!is_array($priorities)) {
                $priorities = array( $priorities );
            }

            foreach ($priorities as $priority) {
                $success = remove_filter( 'the_content', $filter, $priority );
            }
        }
    }

    private function _trimHtmlCommentParts( &$contents )
    {
        $begin = substr( $contents, 0, 5 );
        $end   = substr( $contents, -5 );

        $must_remove_begin = $begin == ' -->';
        if ($must_remove_begin) {
            $contents = substr( $contents, 5 );
        }

        $must_remove_end = $end == '<!-- ';
        if ($must_remove_end) {
            $contents = substr( $contents, 0, -5 );
        }
    }


    private function _matchesUnlockPolicy( $product, $postProduct, $userProduct )
    {
        switch ($product->unlock_policy)
        {
            case 'day':
                $post = get_post( $postProduct->post_id );

                $post_date  = ncore_dbDate( $post->post_date, 'date' );
                $order_date = ncore_dbDate( $userProduct->order_date, 'date' );

                $is_match = $post_date >= $order_date;
                break;

            case 'month':
                $post = get_post( $postProduct->post_id );

                $post_date  = ncore_dbDate( $post->post_date, 'date' );
                $order_date = ncore_dbDate( $userProduct->order_date, '1st_day_of_month' );

                $is_match = $post_date >= $order_date;
                break;

            case 'last_post':
                $post = get_post( $postProduct->post_id );

                $post_dateTime  = ncore_dbDate( $post->post_date, 'full' );
                $order_dateTime = ncore_dbDate( $userProduct->order_date, 'full' );

                if($post_dateTime >= $order_dateTime){
                    $is_match = true;
                    break;
                }

                $pageProductData = $this->api->load->model( 'data/page_product' );
                $where = array( 'product_id' => $product->id, 'is_active' => 'Y' );
                $pageProducts = $pageProductData->getAll( $where );
                $include = [];
                foreach ($pageProducts as $pageProduct) {
                    $include[] = $pageProduct->post_id;
                }

                $args = array(
                    'date_query' => array(
                        array(
                            'before'     => $userProduct->order_date,
                            'inclusive' => false,
                        )
                    ),
                    'post_status' => 'publish',
                    'post_type' => $post->post_type,
                    'posts_per_page' => 1,
                    'order' => 'DESC',
                    'orderby' => 'date',
                );
                if (count($include) > 0) {
                    $args['include'] = $include;
                }

                remove_filter( "pre_get_posts", 'digimember_supress_filter', 100 );
                $items = get_posts($args);
                add_filter( "pre_get_posts", 'digimember_supress_filter', 100 );

                if (count($items) > 0 && $items[0]->ID == $post->ID) {
                    $is_match = true;
                }
                else {
                    $is_match = false;
                }
                break;


            default:
                $is_match = true;
        }

        return $is_match;
    }

    private function _handleAccessWaiver( $product_obj_or_id )
    {
        if (!$product_obj_or_id) {
            return;
        }

        $this->api->load->model( 'data/product' );
        $product = $this->api->product_data->resolveToObj( $product_obj_or_id );
        if (!$product) {
            return;
        }

        $page_id = ncore_retrieve( $product, 'right_of_withdrawal_waiver_page_id' );

        if (!$page_id)
        {
            return;
        }

        $redirect_url = ncore_resolveUrl( $page_id );
        $redirect_url = ncore_addArgs( $redirect_url, array( 'dm_product_id' => $product->id, 'dm_redirect_to' => ncore_currentUrl()) , '&', true );
        ncore_redirect( $redirect_url );
    }
}