/**
     * @param string $clusterIdentifier
     * @return array
     */
    public static function getForbiddenWordsArray ( $clusterIdentifier )
    {
        if (!isset(self::$forbiddenWordsArray))
            self::$forbiddenWordsArray = array();
        
        if ( !isset(self::$forbiddenWordsArray[$clusterIdentifier]) )
        {
            $db         = MMDB::instance();
            $query      = sprintf( "SELECT forbidden_words FROM bo_cluster where identifier='%s'", $clusterIdentifier );
            $results    = $db->arrayQuery($query);
            
            if ( $results )
                $forbiddenWords = trim($results[0]["forbidden_words"]);

            if( !isset($forbiddenWords) || empty($forbiddenWords) )
            {
                self::$forbiddenWordsArray[$clusterIdentifier] = array();
                return array();
            }

            $forbiddenWordsSplit = preg_split( "/(\r\n|\n|\r)/", $forbiddenWords, -1, PREG_SPLIT_NO_EMPTY );
            $tmp = array();
            foreach( $forbiddenWordsSplit as $word )
            {
                if (trim($word) != '')
                    $tmp[] = '-"'.addcslashes(trim($word), '"').'"';
            }
            $forbiddenWordsArray = array_unique( $tmp, SORT_LOCALE_STRING );
            
            self::$forbiddenWordsArray[$clusterIdentifier] = $forbiddenWordsArray;
        }
        
        return self::$forbiddenWordsArray[$clusterIdentifier];
    }
/* @type $cli eZCli */
/* @type $script eZScript */

$now             = time();
$getVisibleQuery = "SELECT contentobject_id FROM publishing_flow WHERE online_date <= $now AND ( offline_date > $now OR offline_date = 0 OR offline_date is null ) AND visibility = 0";
$getHideQuery    = "SELECT contentobject_id FROM publishing_flow WHERE ( offline_date <= $now AND offline_date > 0 OR online_date > $now ) AND visibility = 1";
$db              = eZDB::instance();

$getVisibleResults = $db->arrayQuery($getVisibleQuery);
checkObjectVisibility ($getVisibleResults);

$getHideResults = $db->arrayQuery($getHideQuery);
checkObjectVisibility ($getHideResults);

NodeVisibilityCheck::main();

/**
 * @param array $contentObjectList
 */
function checkObjectVisibility ($contentObjectList)
{
    foreach ( array_keys($contentObjectList) as $index )
    {
        $contentObjectId  = $contentObjectList[$index]['contentobject_id'];

        echo "Checking visibility of object $contentObjectId\n";

        $articleContentObject = eZContentObject::fetch($contentObjectId);

        if ( $articleContentObject && $articleContentObject->attribute('class_identifier') === "article" )
    /**
     * @return array
     */
    public function getResponseSolr()
    {
        $applicationDictionaryRows = $this->prepareConfig();

        $forbiddenWords = NodeVisibilityCheck::getForbiddenWordsArray( $this->_cluster_identifier );
        $queryTerm = count($forbiddenWords) ? implode(' ', $forbiddenWords) : '*:*';

        foreach ( $applicationDictionaryRows as $applicationDictionaryRow )
        {
            // Get application node_id
            $applicationId = $applicationDictionaryRow['application_id'];

            /** @var ApplicationLocalized[] $_localApplication */
            $this->_localApplications[$applicationId] = CacheApplicationTool::buildLocalizedApplicationByApplication( $applicationId );

            $appLocalizedIsProper = ( $this->_localApplications[$applicationId] instanceof ApplicationLocalized );

            if ( !$appLocalizedIsProper )
            {
                eZDebug::writeError( sprintf( 'Cannot fetch localized application %s for cluster %s', $applicationId, $this->_cluster_identifier ), __FILE__ . '::' . __LINE__ );
                continue;
            }

            /* @type $validLanguages array */
            $newsletterStyle = $applicationDictionaryRow['newsletter_style'];
            $clusterSiteIni  = eZINI::fetchFromFile( "extension/{$this->_cluster_identifier}/settings/site.ini.append.php" );
            $validLanguages  = $clusterSiteIni->variable( 'RegionalSettings', 'SiteLanguageList' );

            // Common
            $fq = array(
                'meta_class_identifier_ms:"article"',
                '(attr_archive_date_dt:"1970-01-01T01:00:00Z" OR attr_archive_date_dt:[NOW TO *])',
                'meta_installation_id_ms:'.eZSolr::installationID(),
                'attr_is_invisible_' . $this->_cluster_identifier . '_b:false',
                'meta_language_code_ms:(' . implode( ' OR ', $validLanguages ) . ')',
            );
            
            $taxonomyList = json_decode( $applicationDictionaryRow['taxonomy_filter'], true );

            if(count($taxonomyList) > 0){
                foreach ($taxonomyList as $row) {
                    foreach($row as $taxonomyCategory => $taxonomies){
                        $taxonomies = array_map(function($value) { return '"' . $value . '"'; }, $taxonomies);
                        $fq[] = "subattr_{$taxonomyCategory}___source_id____s: (" . implode(',', $taxonomies) . ')';
                    }
                }
            }

            // NO SDK
            $publisherNodeIds = $this->_localApplications[$applicationId]->publisherNodeIds();
            if(count($publisherNodeIds) == 1 )
            {
                $newsletterStyle = $applicationDictionaryRow['newsletter_style'];

                $fq = array_merge($fq, array(
                    'meta_path_si:' . $publisherNodeIds[0],
                ));
            }
            elseif (count($publisherNodeIds) > 1 )
            {
                $publisherFilter = implode(' OR ', $publisherNodeIds);
                $newsletterStyle = $applicationDictionaryRow['newsletter_style'];

                $fq = array_merge($fq, array(
                        "meta_path_si:($publisherFilter)",
                    ));
            }
            /**
             * SDK Specific treatment; dead code for now
             *
                if ( $this->_localApplications[$applicationId] instanceof SDKApplication )
                {
                    // SDK application
                    $fq = array_merge($fq, array(
                        'subattr_local_application___source_id____s:' . $applicationId,
                        'is_sdk_b:true AND is_newsletter_b:true'
                    ));
                }
             *
             */

            // Solr query parameters
            $rows = 100000;
            $queryParams = array(
                'indent' => 'on',
                'q' => $queryTerm,
                'start' => 0,
                'rows' => $rows,
                'fq' => $fq,
                'fl' => array(
                    'attr_has_image_' . $this->_cluster_identifier . '_bst',
                    'meta_remote_id_ms',
                    'meta_node_id_si',
                    'meta_main_node_id_si',
                    'attr_featured_content_b',
                    'attr_date_dt',
                    'meta_path_string_ms',
                    'meta_language_code_ms',
                    'attr_view_counter_' . $this->_cluster_identifier . '_i',
                    'subattr_speciality___source_id____s',
                    'subattr_customer_type___source_id____s',
                    'meta_current_version_si',
                    'attr_promo_description_t',
                    'attr_author_t',
                    'attr_source_t',
                    'attr_online_date_dt',
                    'attr_headline_s',
                    'subattr_publisher_folder___source_id____s',
                    'attr_promo_headline_s',
                    'attr_'.$this->_cluster_identifier.'_remote_s',
                    'attr_'.$this->_cluster_identifier.'_node_remote_s',
                    //'attr_media_content_image_'.$this->_cluster_identifier.'____ms',
                    //'attr_promo_image_'.$this->_cluster_identifier.'_s',
                    'is_sdk_b',
                    'meta_url_alias_ms',
                    'attr_promo_headline_t',
                    'subattr_publisher_folder___source_id____s',
                    'meta_id_si',
                    'attr_media_content_types_' . ClusterTool::clusterIdentifier() . '_bst',
                    'attr_' . ClusterTool::clusterIdentifier() . '_url_s',
                    'attr_core_content_t',
                    'subattr_download_ressource___expiration_date____dt',
                    'attr_node_remote_s',
                    'attr_media_content_quiz_replies_' . $this->_cluster_identifier . '____ms',
                    'attr_media_content_quiz_points_' . $this->_cluster_identifier . '_i',
                    'attr_media_content_quiz_question_' . $this->_cluster_identifier . '_ms',
                ),
                'qt' => '',
                'explainOther' => '',
                'hl.fl' => '',
                'sort' => $this->_configuration['sort']
            );
            $publisherFilters = $this->_localApplications[$applicationId]->getPublishersFilter();
            if ( $publisherFilters )
            {
                $queryParams['fq'][] = $publisherFilters;
            }

            if ( !empty($this->_customerType) )
            {
                $customerTypeCondition = implode(',', $this->stringArrayToFilterQueryParam($this->_customerType));
                $queryParams['fq'][] = sprintf( 'subattr_customer_type___source_id____s:(%s)', $customerTypeCondition );
            }
            if ( !empty($this->_specialty) )
            {
                $specialtyCondition = implode(',', $this->stringArrayToFilterQueryParam($this->_specialty));
                $queryParams['fq'][] = sprintf( 'subattr_speciality___source_id____s:(%s)', $specialtyCondition );
            }
            if ( $applicationDictionaryRow["publisher"] )
            {
                $queryParams['fq'][] = sprintf( 'subattr_publisher_folder___source_id____s:(%s)', $applicationDictionaryRow["publisher"] );
            }

            if ( $newsletterStyle == 'PICL' )
            {
                $queryParams['fq'][] = 'subattr_media_type___source_id____s:107.2';
            }

            //taxonomies
            $taxonomies = $this->getApplicationTaxonomies($applicationDictionaryRow['feed_id']);
            $firstCategory = reset(array_keys($taxonomies));
            $queryTaxonomies = '';
            foreach($taxonomies as $categorie=>$taxonomie)
            {
                if($categorie != $firstCategory)
                {
                    $queryTaxonomies .= ' AND ';
                }
                $queryTaxonomies .= 'subattr_' . $categorie . '___source_id____s:(' . $taxonomie . ')';
            }

            if($queryTaxonomies != '')
            {
                $queryParams['fq'][] = $queryTaxonomies;
            }

            $queryParamsFqFallback = $queryParams['fq'];
            $rowValueKey           = '';

            // different sort and filters by mechanism
            switch ($applicationDictionaryRow["mechanism"]) {
                case 1:
                    $rowValueKey = 'number_article_list';
                    $queryParams['sort'] = implode( ', ', $queryParams['sort'] );

                    //if begin/end date is set
                    if(!empty($this->_beginDate) && !empty($this->_endDate))
                    {
                        $queryParams['fq'][] = 'attr_online_date_dt:[' . $this->_beginDate . ' TO ' . $this->_endDate . ']';
                    }
                    else
                    {
                        $queryParams['fq'][] = sprintf( 'attr_online_date_dt:[NOW-%sDAY TO *]', $this->_configuration['days'] );
                    }
                    break;
                case 2:
                    $rowValueKey = 'number_article_random';
                    $oneHourRandom = floor(time() / 3600);
                    $queryParams['sort'] = 'attr_' . $oneHourRandom . '_random asc';
                    break;
                case 4:
                    $rowValueKey = 'number_article_last_x';
                    $queryParams['sort'] = "attr_online_date_dt desc";
                    $queryParams['fq'][] = 'attr_online_date_dt:[NOW-7DAY TO *]';

                    //if begin/end date is set
                    if(!empty($this->_beginDate) && !empty($this->_endDate))
                    {
                        $queryParams['fq'][] = 'attr_online_date_dt:[' . $this->_beginDate . ' TO ' . $this->_endDate . ']';
                    }
                    else
                    {
                        $queryParams['fq'][] = sprintf( 'attr_online_date_dt:[NOW-%sDAY TO *]', $this->_configuration['days'] );
                    }
                    break;
                case 5:
                    $rowValueKey = 'number_article_list';
                    if(!empty($this->_beginDate) && !empty($this->_endDate))
                    {
                        $queryParams['fq'][] = 'attr_online_date_dt:[' . $this->_beginDate . ' TO ' . $this->_endDate . ']';
                    }
                    $queryParams['sort'] = implode( ', ', $queryParams['sort'] );
                    if( $applicationDictionaryRow['number_article_ns'] )
                    {  
                        $queryParams['rows'] = (int)$applicationDictionaryRow['number_article_ns'];
                    }
                    else
                    {
                         $queryParams['rows'] = 100;
                    }
                    break;
            }

            if ( (int) $applicationDictionaryRow[$rowValueKey] >= 0 )
            {
                $queryParams['rows'] = (int) $applicationDictionaryRow[$rowValueKey];
            }

            $queryParams['fl'] = implode( ',', $queryParams['fl'] );
            $queryParams['fq'] = implode( ' AND ', $queryParams['fq'] );

            // main fetch solr
            $result = SolrTool::rawSearch( $queryParams, 'php', false );
            if ( !isset( $result['response']['docs'] ) )
            {
                eZDebug::writeError( 'Error from Solr for query : ' . $result['params']['fq'], __FILE__ . '::' . __LINE__ );
                if( php_sapi_name() != 'cli' )
                {
                    header( 'HTTP/1.x 500 Internal Server Error' );
                    eZExecution::cleanExit();
                }
            }
            if ( count($result['response']['docs']) == 0 && $applicationDictionaryRow['mechanism'] == 5)
            {
                $queryParams['fq'] = $queryParamsFqFallback;
                $queryParams['sort'] = array(
                    'attr_featured_content_b desc',
                    'attr_online_date_dt desc',
                );

                $queryParams['sort'] = implode( ', ', $queryParams['sort'] );

                //if begin/end date is set
                if(!empty($this->_beginDate) && !empty($this->_endDate))
                {
                    $queryParams['fq'][] = 'attr_online_date_dt:[' . $this->_beginDate . ' TO ' . $this->_endDate . ']';
                }
                $queryParams['fq'] = implode( ' AND ', $queryParams['fq'] );
                $result = SolrTool::rawSearch( $queryParams, 'php', false );
            }

            // if no result fallback for mechanism 3
            if ( count( $result['response']['docs'] ) == 0 && $applicationDictionaryRow["mechanism"] == 3 )
            {
                $queryParams['fq'] = $queryParamsFqFallback;
                $queryParams['fq'] = implode( ' AND ', $queryParams['fq'] );
                if ( (int) $applicationDictionaryRow['number_article_random'] >= 0 )
                {
                    $queryParams['rows'] = (int) $applicationDictionaryRow['number_article_random'];
                }
                else
                {
                    $queryParams['rows'] = $rows;
                }
                $oneHourRandom = floor(time() / 3600);
                $queryParams['sort'] = 'attr_' . $oneHourRandom . '_random asc';

                $result = SolrTool::rawSearch( $queryParams, 'php', false );
                if ( !isset( $result['response']['docs'] ) )
                {
                    eZDebug::writeError( 'Error from Solr for query : ' . $result['params']['fq'], __FILE__ . '::' . __LINE__ );
                    if( php_sapi_name() != 'cli' )
                    {
                        header( 'HTTP/1.x 500 Internal Server Error' );
                        eZExecution::cleanExit();
                    }
                }
            }

            $articles = $result['response']['docs'];

            // if mechanism 4, we need to have articles also from previous week
            if($applicationDictionaryRow["mechanism"] == 4)
            {
                $queryParams['fq'] = $queryParamsFqFallback;
                $queryParams['fq'][] = 'attr_online_date_dt:[NOW-14DAY TO NOW-7DAY]';
                $oneHourRandom = floor(time());
                $queryParams['sort'] = 'attr_' . $oneHourRandom . '_random asc';

                $queryParams['fq'] = implode( ' AND ', $queryParams['fq'] );
                $queryParams['rows'] = (int) $applicationDictionaryRow['number_article_random_y'];

                $resultRandom = SolrTool::rawSearch( $queryParams, 'php', false );
                if ( !isset( $resultRandom['response']['docs'] ) )
                {
                    eZDebug::writeError( 'Error from Solr for query : ' . $result['params']['fq'], __FILE__ . '::' . __LINE__ );
                    if( php_sapi_name() != 'cli' )
                    {
                        header( 'HTTP/1.x 500 Internal Server Error' );
                        eZExecution::cleanExit();
                    }
                }

                if(count($resultRandom['response']['docs'])) {
                    $articles = array_merge($articles, $resultRandom['response']['docs']);
                }
            }

            $this->_applicationsData[$applicationId][] = array(
                'articles'                    => $articles,
                'applicationDictionaryRow'    => $applicationDictionaryRow
            );
        }

        return $this->_applicationsData;
    }
            if ( $i%100 == 0 )
                $cli->notice( "$i nodes have been treated." );

            $i++;

            if ( !isset($result) || !isset($result['main_node_id']) )
            {
                $cli->warning("{$result['main_node_id']} is not a valid node.");
                continue;
            }

            $row = array(
                'param'   => $result['main_node_id'],
                'id'      => $result['main_node_id'],
                'created' => 1,
                'manual'  => true,
            );

            NodeVisibilityCheck::updateNodeVisibility($row);

            eZContentObject::clearCache();
        }

        $offset+= $limit;
    }

    $cli->notice( "Treatment completed successfully on cluster $cluster; $i nodes treated." );
}

$script->shutdown();