<commit_before><?php

/**
 *  \details &copy; 2011  Open Ximdex Evolution SL [http://www.ximdex.org]
 *
 *  Ximdex a Semantic Content Management System (CMS)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published
 *  by the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  See the Affero GNU General Public License for more details.
 *  You should have received a copy of the Affero GNU General Public License
 *  version 3 along with Ximdex (see LICENSE file).
 *
 *  If not, visit http://gnu.org/licenses/agpl-3.0.html.
 *
 * @author <NAME> <<EMAIL>>
 * @version $Revision$
 */

use Ximdex\Logger;
use Ximdex\Models\Node;
use Ximdex\Models\User;
use Ximdex\Models\FastTraverse;
use Ximdex\NodeTypes\NodeTypeConstants;

\Ximdex\Modules\Manager::file('/inc/manager/BatchManager.class.php', 'ximSYNC');
\Ximdex\Modules\Manager::file('/inc/model/NodesToPublish.class.php', 'ximSYNC');
\Ximdex\Modules\Manager::file( '/conf/synchro_conf.php', 'ximSYNC');

/**
 * @brief Manages the phases previous and later of the publication process.
 */
class SyncManager
{
    // State flags.
    public $workflow;
    public $deleteOld;
    public $markEnd;
    public $linked;
    public $type;
    public $mail;
    private $lastPublished;
    private $publicateSection;
    private $level;
    private $docsToPublishByLevel = array();
    private $computedDocsToPublish = array();
    private $pendingDocsToPublish = array();

    function __construct()
    {
        // Default values for state flags.
        $this->setFlag('workflow', true);
        $this->setFlag('deleteOld', false);
        $this->setFlag('markEnd', false);
        $this->setFlag('linked', false);
        $this->setFlag('recurrence', false);
        $this->setFlag('type', 'core');
        $this->setFlag('mail', false);
        $this->setFlag('deeplevel', DEEP_LEVEL < 0 ? 1 : DEEP_LEVEL);
        $this->setFlag('globalForcePublication', FORCE_PUBLICATION);
    }

    /**
     * Sets the value of any variable.
     *  
     * @param string key
     * @param unknown value
     */
    function setFlag($key, $value)
    {
        $this->$key = $value;
    }

    /**
     * Gets the value of any variable.
     * 
     * @param string key
     */
    function getFlag($key)
    {
        if (isset($this->$key)) {
            return $this->$key;
        }
        return NULL;
    }

    private function buildPublishingDependencies($idNode, $params)
    {
        $this->pendingDocsToPublish = $this->computedDocsToPublish = array();
        $this->pendingDocsToPublish[] = $idNode;
        $this->docsToPublishByLevel["$idNode"] = 0;
        while (!empty($this->pendingDocsToPublish)) {
            $nodeId = array_shift($this->pendingDocsToPublish);
            if ($this->hasDependences($nodeId, $params)) {
                continue;
            }
            $this->computedDocsToPublish[] = $nodeId;
        }
        return $this->computedDocsToPublish;
    }
    
    /**
     * Get the nodes over a section, only the publishable and no folder type ones
     * 
     * @param Node $node
     * @param array $nodes
     * @param int $level
     * @return bool
     */
    private function buildPublishingSection(Node $node, array & $nodes, int $level = null) : bool
    {
        $nodeTypeFlags = ['IsPublishable' => true, 'IsFolder' => false];
        if ($level !== null) {
            
            // Publication of the section until a given level
            if ($node->GetNodeType() == NodeTypeConstants::SERVER or $node->GetNodeType() == NodeTypeConstants::SECTION) {
                
                // Server and sections nodes must increment one level more
                $level++;
            }
        }
        // First search for structural assets nodes
        $nodeTypeFlagsAux = $nodeTypeFlags + ['IsStructuredDocument' => false];
        $assetsChildren = FastTraverse::get_children($node->GetID(), null, $level, null, $nodeTypeFlagsAux);
        
        // Then search the structured documents with one plus level to include at the end of the pool
        $nodeTypeFlagsAux = $nodeTypeFlags + ['IsStructuredDocument' => true];
        if ($level) {
            $level++;
        }
        $documentsChildren = FastTraverse::get_children($node->GetID(), null, $level, null, $nodeTypeFlagsAux);
        
        // Combine the two pools
        $children = array_merge_recursive($assetsChildren, $documentsChildren);
        if ($children === false) {
            return false;
        }
        foreach ($children as $level => $levelNodes) {
            foreach ($levelNodes as $id) {
                $nodes[] = $id;
                $this->docsToPublishByLevel[$id] = $level;
            }
        }
        return true;
    }
    

    /**
     * Gets the Nodes that must be published with the current Node and calls the methods for build the Batchs.
     * 
     * @param int $nodeId
     * @param int up
     * @param int down
     * @return array|null
     */
    function pushDocInPublishingPool(int $idNode, int $up, int $down = null)
    {
        if (!$idNode) {
            Logger::error(_("Pushdocinpool - Empty IdNode"));
            return NULL;
        }
        $node = new Node($idNode);
        if (!$node->GetID()) {
            Logger::error(sprintf(_("Node %s does not exist"), $idNode));
            return NULL;
        }
        $publicateSection = $this->getFlag('publicateSection');
        if ($publicateSection) {
            
            // Obtain all the whole children above the given section
            $level = $this->getFlag('level');
            $docsToPublish = [];
            if (!$this->buildPublishingSection($node, $docsToPublish, $level)) {
                return null;
            }
        }
        else {
            // Flags for dependencies
            $params = [];
            $params['withstructure'] = ($this->getFlag('structure') === false) ? false : true;
            $params['deeplevel'] = $this->getFlag('deeplevel');
            $docsToPublish = $this->buildPublishingDependencies($idNode, $params);
        }
        if ($node->nodeType->get('IsPublishable') == '1') {
            if (sizeof($docsToPublish) > 0) {
                $docsToPublish = array_unique(array_merge(array($idNode), $docsToPublish));
            }
            else {
                $docsToPublish = array($idNode);
                $this->docsToPublishByLevel = array($idNode);
            }
        }
        else {
            return array();
        }
        $userID = \Ximdex\Runtime\Session::get('userID');
        $force = $this->getFlag('globalForcePublication') ? true : $this->getFlag("force");
        $lastPublishedDocument = $this->getFlag("lastPublished");
        foreach ($docsToPublish as $idDoc) {
            if (!array_key_exists($idDoc, $this->docsToPublishByLevel)) {
                continue;
            }
            $deepLevel = $this->docsToPublishByLevel[$idDoc];

            // Dependencies won't be expired
            if ($idNode == $idDoc) {
                $ntp = NodesToPublish::create($idDoc, $idNode, $up, $down, $userID, $force, $lastPublishedDocument, $deepLevel);
            }
            else {
                $ntp = NodesToPublish::create($idDoc, $idNode, $up, null, $userID, $force, $lastPublishedDocument, $deepLevel);
            }
        }
        if ($this->getFlag('mail')) {
            $this->sendMail($idNode, $type, $up, $down);
        }

        // Exec batchmanagerdaemon in background and get its pid (in case we needed in the future)
        $cmd = 'php ' . XIMDEX_ROOT_PATH . '/bootstrap.php  modules/ximSYNC/inc/manager/BatchManagerDaemon.php';
        $pid = shell_exec(sprintf("%s > /dev/null & echo $!", $cmd));
        return $docsToPublish;
    }

    /**
     * @param $nodeId
     * @param $params
     * @return mixed
     */
    public function hasDependences($nodeId, $params)
    {
        $deepLevel = $params["deeplevel"];
        if (!isset($this->docsToPublishByLevel["$nodeId"])) {
            return false;
        }
        $currentDeepLevel = $this->docsToPublishByLevel["$nodeId"] + 1;
        if ($deepLevel != -1 && $deepLevel < $currentDeepLevel) {
            return false;
        }
        $node = new Node($nodeId);
        $nodeDependences = $node->class->getPublishabledDeps($params);
        if (!isset($nodeDependences) || empty($nodeDependences)) {
            return false;
        }
        $pending = array_values(array_diff($nodeDependences, $this->pendingDocsToPublish, $this->computedDocsToPublish));
        if (empty($pending)) {
            return false;
        }
        else {
            $idDoc = $pending[0];
            while($idDoc == $nodeId){
                $res = array_shift($pending);
                if (!empty($pending)) {
                    $idDoc = $pending[0];
                }
                else {
                    return false;
                }
            }
            $this->docsToPublishByLevel["$idDoc"] = $currentDeepLevel;
            $this->pendingDocsToPublish = array_merge([$idDoc, $nodeId], $this->pendingDocsToPublish);
            return true;
        }
    }

    private function sendMail($nodeID, $type, $up, $down)
    {
        $node = new node($nodeID);
        $name = $node->Get('Name');
        $msg = sprintf(_("Node %s is going to be published"), $name);
        if (!$down) {
            $downString = _('Undetermined');
        }
        else {
            $downString = date('d-m-Y H:i:s', $down);
        }
        $msg .= "\n" . _("Publication date") . ": " . date('d-m-Y H:i:s', $up);
        $msg .= "\n" . _("Expiration date") . ":" . " $downString";
        $user = new User(301);
        $email = $user->Get('Email');
        $mail = new \Ximdex\Utils\Mail();
        $mail->addAddress($email);
        $mail->Subject = _("Publication of") . " $name";
        $mail->Body = $msg;
        $mail->Send();
    }
}<commit_msg>Generation of the publication pool for sections with priority<commit_after><?php

/**
 *  \details &copy; 2011  Open Ximdex Evolution SL [http://www.ximdex.org]
 *
 *  Ximdex a Semantic Content Management System (CMS)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published
 *  by the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  See the Affero GNU General Public License for more details.
 *  You should have received a copy of the Affero GNU General Public License
 *  version 3 along with Ximdex (see LICENSE file).
 *
 *  If not, visit http://gnu.org/licenses/agpl-3.0.html.
 *
 * @author Ximdex DevTeam <<EMAIL>>
 * @version $Revision$
 */

use Ximdex\Logger;
use Ximdex\Models\Node;
use Ximdex\Models\User;
use Ximdex\Models\FastTraverse;
use Ximdex\NodeTypes\NodeTypeConstants;

\Ximdex\Modules\Manager::file('/inc/manager/BatchManager.class.php', 'ximSYNC');
\Ximdex\Modules\Manager::file('/inc/model/NodesToPublish.class.php', 'ximSYNC');
\Ximdex\Modules\Manager::file( '/conf/synchro_conf.php', 'ximSYNC');

/**
 * @brief Manages the phases previous and later of the publication process.
 */
class SyncManager
{
    // State flags.
    public $workflow;
    public $deleteOld;
    public $markEnd;
    public $linked;
    public $type;
    public $mail;
    private $lastPublished;
    private $publicateSection;
    private $level;
    private $docsToPublishByLevel = array();
    private $computedDocsToPublish = array();
    private $pendingDocsToPublish = array();

    function __construct()
    {
        // Default values for state flags.
        $this->setFlag('workflow', true);
        $this->setFlag('deleteOld', false);
        $this->setFlag('markEnd', false);
        $this->setFlag('linked', false);
        $this->setFlag('recurrence', false);
        $this->setFlag('type', 'core');
        $this->setFlag('mail', false);
        $this->setFlag('deeplevel', DEEP_LEVEL < 0 ? 1 : DEEP_LEVEL);
        $this->setFlag('globalForcePublication', FORCE_PUBLICATION);
    }

    /**
     * Sets the value of any variable.
     *  
     * @param string key
     * @param unknown value
     */
    function setFlag($key, $value)
    {
        $this->$key = $value;
    }

    /**
     * Gets the value of any variable.
     * 
     * @param string key
     */
    function getFlag($key)
    {
        if (isset($this->$key)) {
            return $this->$key;
        }
        return NULL;
    }

    private function buildPublishingDependencies($idNode, $params)
    {
        $this->pendingDocsToPublish = $this->computedDocsToPublish = array();
        $this->pendingDocsToPublish[] = $idNode;
        $this->docsToPublishByLevel["$idNode"] = 0;
        while (!empty($this->pendingDocsToPublish)) {
            $nodeId = array_shift($this->pendingDocsToPublish);
            if ($this->hasDependences($nodeId, $params)) {
                continue;
            }
            $this->computedDocsToPublish[] = $nodeId;
        }
        return $this->computedDocsToPublish;
    }
    
    /**
     * Get the nodes over a section, only the publishable and no folder type ones
     * 
     * @param Node $node
     * @param array $nodes
     * @param int $level
     * @return bool
     */
    private function buildPublishingSection(Node $node, array & $nodes, int $level = null) : bool
    {
        $nodeTypeFlags = ['IsPublishable' => true, 'IsFolder' => false];
        if ($level !== null) {
            
            // Publication of the section until a given level
            if ($node->GetNodeType() == NodeTypeConstants::SERVER or $node->GetNodeType() == NodeTypeConstants::SECTION) {
                
                // Server and sections nodes must increment one level more
                $level++;
            }
        }
        // First search for structural assets nodes
        $nodeTypeFlagsAux = $nodeTypeFlags + ['IsStructuredDocument' => false];
        $assetsChildren = FastTraverse::get_children($node->GetID(), null, $level, null, $nodeTypeFlagsAux);
        if ($assetsChildren === false) {
            return false;
        }
        foreach ($assetsChildren as $nodeLevel => $levelNodes) {
            $nodeLevel += 100;
            foreach ($levelNodes as $id) {
                $nodes[] = $id;
                $this->docsToPublishByLevel[$id] = $nodeLevel;
            }
        }
        
        // Then search the structured documents with one plus level to include at the end of the pool
        $nodeTypeFlagsAux = $nodeTypeFlags + ['IsStructuredDocument' => true];
        if ($level) {
            $level++;
        }
        $documentsChildren = FastTraverse::get_children($node->GetID(), null, $level, null, $nodeTypeFlagsAux);
        if ($documentsChildren === false) {
            return false;
        }
        foreach ($documentsChildren as $nodeLevel => $levelNodes) {
            foreach ($levelNodes as $id) {
                $nodes[] = $id;
                $this->docsToPublishByLevel[$id] = $nodeLevel;
            }
        }
        return true;
    }
    

    /**
     * Gets the Nodes that must be published with the current Node and calls the methods for build the Batchs.
     * 
     * @param int $nodeId
     * @param int up
     * @param int down
     * @return array|null
     */
    function pushDocInPublishingPool(int $idNode, int $up, int $down = null)
    {
        if (!$idNode) {
            Logger::error(_("Pushdocinpool - Empty IdNode"));
            return NULL;
        }
        $node = new Node($idNode);
        if (!$node->GetID()) {
            Logger::error(sprintf(_("Node %s does not exist"), $idNode));
            return NULL;
        }
        $publicateSection = $this->getFlag('publicateSection');
        if ($publicateSection) {
            
            // Obtain all the whole children above the given section
            $level = $this->getFlag('level');
            $docsToPublish = [];
            if (!$this->buildPublishingSection($node, $docsToPublish, $level)) {
                return null;
            }
        }
        else {
            
            // Flags for dependencies
            $params = [];
            $params['withstructure'] = ($this->getFlag('structure') === false) ? false : true;
            $params['deeplevel'] = $this->getFlag('deeplevel');
            $docsToPublish = $this->buildPublishingDependencies($idNode, $params);
        }
        if ($node->nodeType->get('IsPublishable') == '1') {
            if (sizeof($docsToPublish) > 0) {
                $docsToPublish = array_unique(array_merge(array($idNode), $docsToPublish));
            }
            else {
                $docsToPublish = array($idNode);
                $this->docsToPublishByLevel = array($idNode);
            }
        }
        else {
            return array();
        }
        $userID = \Ximdex\Runtime\Session::get('userID');
        $force = $this->getFlag('globalForcePublication') ? true : $this->getFlag("force");
        $lastPublishedDocument = $this->getFlag("lastPublished");
        foreach ($docsToPublish as $idDoc) {
            if (!array_key_exists($idDoc, $this->docsToPublishByLevel)) {
                continue;
            }
            $deepLevel = $this->docsToPublishByLevel[$idDoc];

            // Dependencies won't be expired
            if ($idNode == $idDoc) {
                $ntp = NodesToPublish::create($idDoc, $idNode, $up, $down, $userID, $force, $lastPublishedDocument, $deepLevel);
            }
            else {
                $ntp = NodesToPublish::create($idDoc, $idNode, $up, null, $userID, $force, $lastPublishedDocument, $deepLevel);
            }
        }
        if ($this->getFlag('mail')) {
            $this->sendMail($idNode, $type, $up, $down);
        }

        // Exec batchmanagerdaemon in background and get its pid (in case we needed in the future)
        $cmd = 'php ' . XIMDEX_ROOT_PATH . '/bootstrap.php  modules/ximSYNC/inc/manager/BatchManagerDaemon.php';
        $pid = shell_exec(sprintf("%s > /dev/null & echo $!", $cmd));
        return $docsToPublish;
    }

    /**
     * @param $nodeId
     * @param $params
     * @return mixed
     */
    public function hasDependences($nodeId, $params)
    {
        $deepLevel = $params["deeplevel"];
        if (!isset($this->docsToPublishByLevel["$nodeId"])) {
            return false;
        }
        $currentDeepLevel = $this->docsToPublishByLevel["$nodeId"] + 1;
        if ($deepLevel != -1 && $deepLevel < $currentDeepLevel) {
            return false;
        }
        $node = new Node($nodeId);
        $nodeDependences = $node->class->getPublishabledDeps($params);
        if (!isset($nodeDependences) || empty($nodeDependences)) {
            return false;
        }
        $pending = array_values(array_diff($nodeDependences, $this->pendingDocsToPublish, $this->computedDocsToPublish));
        if (empty($pending)) {
            return false;
        }
        else {
            $idDoc = $pending[0];
            while($idDoc == $nodeId){
                $res = array_shift($pending);
                if (!empty($pending)) {
                    $idDoc = $pending[0];
                }
                else {
                    return false;
                }
            }
            $this->docsToPublishByLevel["$idDoc"] = $currentDeepLevel;
            $this->pendingDocsToPublish = array_merge([$idDoc, $nodeId], $this->pendingDocsToPublish);
            return true;
        }
    }

    private function sendMail($nodeID, $type, $up, $down)
    {
        $node = new node($nodeID);
        $name = $node->Get('Name');
        $msg = sprintf(_("Node %s is going to be published"), $name);
        if (!$down) {
            $downString = _('Undetermined');
        }
        else {
            $downString = date('d-m-Y H:i:s', $down);
        }
        $msg .= "\n" . _("Publication date") . ": " . date('d-m-Y H:i:s', $up);
        $msg .= "\n" . _("Expiration date") . ":" . " $downString";
        $user = new User(301);
        $email = $user->Get('Email');
        $mail = new \Ximdex\Utils\Mail();
        $mail->addAddress($email);
        $mail->Subject = _("Publication of") . " $name";
        $mail->Body = $msg;
        $mail->Send();
    }
}
{'fraction_non_alphanumeric': 0.13617285128302967, 'fraction_numerical': 0.002050939616521988, 'mean_word_length': 2.11129247662858, 'pattern_counts': {'":': 4, '<': 14, '<?xml version=': 0, '>': 140, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3131909', 'n_tokens_mistral': 6187, 'n_tokens_neox': 6066, 'n_words': 1488}