<?php
/**
 * CONTENT MANAGER CLASS
 *
 * @author Rihards Silins
 * @copyright MTC Media Ltd
 * @version 5 17/11/2016
 * @access public
 *
 * MTC Media's CMS2+ Content Manager class.
 */
use Mtc\Cms\Models\PageListItemData;
use Mtc\Cms\Models\PageListItem;
use Mtc\Cms\Models\PageList;
use Mtc\Cms\Models\Page as CmsPage;
use Mtc\Cms\Models\Node;
use Illuminate\Support\Facades\Schema;
use Mtc\Cms\Helpers\Navigation;
use Mtc\Cms\Handlers\VersioningHandler;
use Mtc\Cms\Handlers\TemplateHandler;
use Illuminate\Support\Facades\Event;
use MtcPharmacy\Multisite\Classes\MultisiteManager;

Class ContentManager {

    public $controller_path = CMS_CONTROLLER_PATH;
    public $template_path = CMS_TEMPLATE_PATH;
    public $layout_path = CMS_LAYOUT_PATH;

    public static $pagedata_types = [
        'checkbox' => 'Checkbox',
        'colorpicker' => 'Color Picker',
        'coordinates' => 'Coordinates',
        'datetime' => 'Date & Time',
        'fileU5' => 'File Input',
        'imageU5' => 'Image Input',
        'fileMU5' => 'Multi-Upload Files',
        'imageMU5' => 'Multi-Upload Images',
        'select' => 'Select Input',
        'gallery' => 'Simple Image Gallery',
        'textarea.simpleTinymce' => 'Simple TintMCE Text Box',
        'tags' => 'Tags',
        'text' => 'Text Input',
        'textarea' => 'Text Box',
    ];

    /**
     * @param mixed[] page_load_init_vars
     * Variables for when initating a front cms page.
     */
    public $page_load_init_vars = array(
        'cache_html'        => false,
        'page_id_to_load'   => false,
        'not_found_page'    => false,
        'uri_parts'         => array(
            'id_param' => null
        ),
        'language'          => ""
    );

    /**
     * ContentManager property that allows plugins to add custom pagedata type properties
     *   if the custom pagedata type has other custom properties other than just type.
     *
     * @var mixed[] multi-dimensional array of properties
     *
     *  Example usage in plugin loader.php (adding a conditions padata type)
     *  ContentManager::$pagedata_properties['conditions'] = array(
     *          0 => array(
     *              'items' => array(
     *                 0 => array(
     *                      'data' => array(
     *                          0 => array(
     *                              'type' => 'text',
     *                              'css_class' => 'conditions'
     *                          ),
     *                      ),
     *                  ),
     *               ),
     *          ),
     *      );
     *
     * When adding do not forget to add it also to ::pagedata_types
     *  property so it shows up in the "Add New Pagedata" dialogue select box.
     * ContentManager::$pagedata_types['conditions'] = 'Conditions';
     */
    public static $pagedata_properties = [];

    /**
     * Array of custom list types to be rendered/skipped
     * @static array $pagelist_render_rules
     */
    public static $pagelist_render_rules = [
        'render' => [],
        'skip' => [],
    ];

    // Function that loads in the page object when page_id is found
    public function loadPage($page_id, $language) {
        $page = new Page();
        $page->setLanguage($language);
        $page->Get($page_id);
        HooksAdapter::do_action('load_page', $page);
        return $page;
    }

    function CreateNewPage($sub_id = 0) {

        // create page
        $page = new Page();
        $page->type = "temp";
        $page->sub_id = $sub_id;
        $page->published = 0;
        $page->allow_delete = (CMS_ALLOW_DELETE === true ? 1 : 0);
        $page->allow_update = (CMS_ALLOW_UPDATE === true ? 1 : 0);
        $page->Save();

        // init new pagedata
        $new_pagedata = new Pagedata();

        $new_pagedata->page_id = $page->id;

        $new_pagedata->pagedata = array(
            0 => array(
                'name' => "Content title",
                'items' => array(
                    0 => array(
                        'data' => array(
                            0 => array(
                                'name' => "Content title",
                                'type' => "text",
                            )
                        )
                    )
                )
            ),
            1 => array(
                'name' => "Content",
                'items' => array(
                    0 => array(
                        'data' => array(
                            0 => array(
                                'name' => "Content",
                                'type' => "textarea",
                                'css_class' => "tinymce",
                            )
                        )
                    )
                )
            )
        );

        $new_pagedata->Save();

        return $page->id;
    }


    public static function getPageBySlug(string $slug)
    {
        $candidate_pages = CmsPage::where('slug', $slug)->get();
		
		//dd($candidate_pages);
		
		$page_to_load = self::getPageToLoad($candidate_pages);

        return $page_to_load;
    }


    public static function getContactPageId()
    {
        $candidate_pages = CmsPage::where('contact_page', 1)->get();
        
		$page_to_load = self::getPageToLoad($candidate_pages);

        return $page_to_load->id;
    }

    /**
     * ContentManager::getFrontPageId()
     * Returns frontpage page id
     * @return int frontpage_page_id
     */
    public static function getFrontPageId() {

        $candidate_pages = CmsPage::where('frontpage', 1)
			->where('id', '!=', CMS_ROOT_PAGE_ID)
			->get();

        $page_to_load = self::getPageToLoad($candidate_pages);

        return $page_to_load->id;
    }
	
	protected static function getPageToLoad($candidate_pages) 
	{
		$root_page = CmsPage::find(CMS_ROOT_PAGE_ID);
        $mm = new MultisiteManager($root_page);
        $display_root_page = $mm->getDisplayEntity();

        $page_to_load = null;
			

        foreach ($candidate_pages as $candidate_page) {
            $candidate_root_page = $candidate_page->getAncestors()->last();
            if ($candidate_root_page->id == $display_root_page->id) {
                $page_to_load = $candidate_page;
                break;
            }
        }

        if (empty($page_to_load)) {
            throw new \Exception("Could not find page to load.");
        }
		
		return $page_to_load;
	}

    // Check if layout exists in any layout directories inside twig locations
    function layout_exists($layout_name) {
        global $twig_loader;

        $layout_path = '/layouts' . $layout_name;

        foreach ($twig_loader->getPaths() as $twig_path) {

            if (file_exists($twig_path . $controller_filename)) {
                return true;
            }

        }

        return false;
    }

    // Check if controller exists in controllers directory inside twig locations
    function controller_exists($view_name) {

        global $twig_loader;

        $controller_filename = "/controller." . $view_name . '.php';

        foreach ($twig_loader->getPaths() as $twig_path) {

            if (file_exists($twig_path . '/controllers' . $controller_filename)) {
                return true;
            }

        }

        return false;
    }

    // return the full path of the controller found
    function get_controller($view_name) {

        global $twig_loader;

        $controller_filename = "/controller." . $view_name . '.php';

        foreach ($twig_loader->getPaths() as $twig_path) {

            if (file_exists($twig_path . '/controllers' . $controller_filename)) {
                return $twig_path . '/controllers' . $controller_filename;
            }

        }

        return false;

    }

    // Check if template exists in templates directory inside twig locations
    function template_exists($view_name) {

        global $twig_loader;

        $template_filename = '/pages' . $view_name;

        foreach ($twig_loader->getPaths() as $twig_path) {

            if (file_exists($twig_path . $template_filename)) {
                return true;
            }

        }

        return false;

    }

    public function URIParts($REQUEST_URI) {
        $raw_uri = $REQUEST_URI;

        $search_for = CMS_ROOT_PAGE_SLUG . '/' . CMS_ROOT_PAGE_SLUG . '/';
        $replace_with = CMS_ROOT_PAGE_SLUG . '/';
        $raw_uri = str_replace($search_for, $replace_with, $raw_uri);

        $id_param = null;
        // Get the number if such exists at the end of the url path (domain.com/people/person/5 = 5)
        if (preg_match("/\\/([0-9]+)\\/?$/", $raw_uri, $matches)) {
            $id_param = $matches[1];
            $raw_uri = substr($raw_uri, 0, strlen($raw_uri) - strlen($matches[0]));
        }

        $language = "";
        if (defined('CMS_MULTI_LANG') && CMS_MULTI_LANG === true && CMS_MULTI_LANG_SWITCH_MODE === 'URL_SUBDIR') {
            $language = $this->getValidLanguageCodeFromURLSubDirectory($raw_uri);
            if ($language != "") {
                $raw_uri = substr($raw_uri, strlen($language), strlen($raw_uri));
            }
        }
        // Get all url path slugs (domain.com/people/person/5 = people, person)
        preg_match_all("/\\/([0-9a-z\-]{1,})/", $raw_uri, $matches);

        return array('slugs' => array_reverse($matches[1]), 'id_param' => $id_param, 'language' => $language);

    }

    /**
     * ContentManager::processURIPartsToPageId()
     * If page not found. Look if there's a close parent
     * @param string[] $uri_parts URI parts
     * @param bool $wildcard whether or not to look for the closes parent aswell
     * @return int/book $page_id page id or false on failure
     */
    public function processURIPartsToPageId($uri_parts, $wildcard = false) {
        $pdo = new CmsPdo();

        $query = "
        SELECT  `id`
        FROM    `pages`
        WHERE   `slug`      =   '" . CmsPdo::cleanDB($uri_parts[0]) . "'
        AND     `published` =   1
        AND     `type`      =   'default'
        SUBQUERY ";

        for ($i = 1; $i < count($uri_parts); $i++) {
            $subquery = "
            AND `sub_id` IN
                (
                SELECT `id`
                FROM    `pages`
                WHERE   `slug`      =   '" . CmsPdo::cleanDB($uri_parts[$i]) . "'
                AND     `published` =   1
                AND     `type`      =   'default'
                SUBQUERY
                ) ";
            $query = str_replace("SUBQUERY", $subquery, $query);
        }

        $query = str_replace("SUBQUERY", "", $query);
        $pdo->run($query);

        $root_page = CmsPage::find(CMS_ROOT_PAGE_ID);
        $mm = new MultisiteManager($root_page);
        $display_root_page = $mm->getDisplayEntity();

        $page_to_load = null;
        while ($row = $pdo->fetch_array()) {
            $candidate_page = CmsPage::find($row['id']);

            $candidate_root_page = $candidate_page->getAncestors()->last();
            if ($candidate_root_page->id == $display_root_page->id) {
                $page_to_load = $candidate_page;
                break;
            }
        }

        if ($page_to_load) {
            return $page_to_load->id;
        }

        // nothing found.
        if ($wildcard) {
            exit('wildcard?');
            return $this->processURIPartsToPageIdWithWildcard($uri_parts);
        }

        // we shall have to do a 404
        return false;
    }

    /**
     * ContentManager::processURIPartsToPageIdWithWildcard()
     * If page not found. Look if there's a close parent
     * @param string[] $uri_parts URI parts
     * @return int/book $page_id page id or false on failure
     */
    public function processURIPartsToPageIdWithWildcard($uri_parts) {
        $pdo = new CmsPdo();

        for ($i = 1; $i < count($uri_parts); $i++) {
            $params = array();
            $params[] = $uri_parts[$i];
            $query = "
            SELECT  `id`
            FROM    `pages`
            WHERE   `slug`      =   ?
            AND     `published` =   1
            AND     `type`      = 'default'
            SUBQUERY ";
            // Attach all slugs to the query to find the correct page from the url path.
            for ($j = ($i+1); $j < count($uri_parts); $j++) {
                $subquery = "
                AND `sub_id` IN
                    (
                    SELECT `id`
                    FROM    `pages`
                    WHERE   `slug`      =   ?
                    AND     `published` =   1
                    AND     `type`      = 'default'
                    SUBQUERY
                    ) ";
                $params[] = $uri_parts[$j];
                $query = str_replace("SUBQUERY", $subquery, $query);
            }


            // last 'page' has to have sub_id 0 or a parent that is a frontpage
            // Frontpage must never be a non level 0 page !
            $query = str_replace("SUBQUERY", "AND (`sub_id` = 0 OR `sub_id` = ( SELECT `id` FROM `pages` WHERE `frontpage` = 1 AND `type` = 'default')) ", $query);
            $pdo->run($query, $params);

            if ($pdo->num_rows() > 0) {
                $data = $pdo->fetch_array();
                return $data['id'];
            }
        }
        return false;
    }

    // faster alternative if cahce is enabled and set
    public function getPageIdFromCacheByUrl($url) {

        $pdo = new CmsPdo();

        $param = array(':url' => $url);

        $query = "
        SELECT  `page_id`
        FROM    `page_cache`
        WHERE   `url` = :url
        ";

        $pdo->run($query, $param);

        if ($pdo->num_rows() == 0) {

            return false;
        }

        $data = $pdo->fetch_array();

        return $data['page_id'];
    }

    /**
     * ContentManager::get_fullcms_404_page()
     * Returns the 404 page id
     * @return int / bool 404_page_id
     */
    public function get_fullcms_404_page() {
        $pdo = new CmsPdo();
        $query = "SELECT `id` FROM `pages` WHERE `slug` = '404' AND `published` = 1 AND `type` = 'default' LIMIT 1 ";
        $pdo->run($query);
        if ($pdo->num_rows() > 0) {
            $data = $pdo->fetch_array();
            return $data['id'];
        }
        return false;
    }

    // Process string to slug format
    public function filterSlug($resource) {
        $result = strtolower($resource);
        $result = Util::remove_accents($result);
        // everything to lowercase

        if ($result != "404" && preg_match("/^[0-9]+$/", $result)) {
            $result = Util::number_to_word($result);
        }

        // special replacement
        $result = preg_replace('/&/', 'and', $result);
        // replace & with and
        $result = preg_replace('/ \\/ /', ' ', $result);
        $result = preg_replace('/\\//', ' ', $result);
        $result = preg_replace('/  /', ' ', $result);
        $result = preg_replace('/   /', ' ', $result);

        $result = trim($result);
        // trim side spaces
        $result = mb_convert_encoding($result, 'US-ASCII', 'UTF-8');
        // convert utf chars to ascii
        $result = preg_replace("/[^a-z0-9 _-]/", "", trim($result));
        // remove all left unfriendly characters
        $result = preg_replace('/\s|_/', '-', $result);
        // replace spaces and '_' with '-''
        return $result;
    }

    public function generateSlug($title = "", $slug = "", $page_id = 0, $page_sub_id = 0) {

        // If provided slug is empty - get the slug resource from title
        if (empty($slug)) {
            $slug = $title;
        }

        $slug = $this->filterSlug($slug);
        // sanitize slug

        $db = new CmsPdo();

        // If there is - check if the current slug has a number at the end
        $numerication = false;
        $base = $slug;
        $end = '';

        if (preg_match('/^(.+)\-([0-9]+)$/', $slug, $mtch) == 1) {
            $numerication = true;
            $base = $mtch[1];
            $end = $mtch[2];
        }

        $good_slug = false;
        if ($slug and CMS_ALLOW_DUPLICATE_SLUGS) {
            $good_slug = true;
        }

        // check if match
        // if so increment the numeration
        do {
            $slug = $base . ($numerication ? '-' : '') . $end;
            // check
            $query = "SELECT `id` FROM `pages` WHERE `slug` = :slug AND `sub_id` = :page_sub_id  AND `id` != :page_id AND `type` = 'default' LIMIT 1";

            $params = array(
                ':slug' => $slug,
                ':page_sub_id' => $page_sub_id,
                ':page_id' => $page_id,
            );

            $db ->run($query, $params);

            if ($db->num_rows() > 0) {
                // exists
                if ($numerication) {
                    $end++;
                } else {
                    $numerication = true;
                    $end = 2;
                }
            } else {
                $good_slug = true;
                // Check if there are any hooked in events that consider this not a good slug.
                $event_results = Event::dispatch(
                    __CLASS__ . '/' . __FUNCTION__ . ':before_approving_slug_as_free',
                    [
                        $page_sub_id,
                        $slug
                    ]
                );
                if (!empty($event_results)) {
                    foreach ($event_results as $event_result) {
                        if ($event_result === 'false') {
                            $good_slug = false;
                            // exists
                            if ($numerication) {
                                $end++;
                            } else {
                                $numerication = true;
                                $end = 2;
                            }
                            break;
                        }
                    }
                }
            }
        } while ( !$good_slug );

        return $slug;

    }

    /**
     * Generate URL using a provided slug for the current page without persisting it.
     * This mirrors Page::generate_url but substitutes the leaf slug.
     *
     * @param int $page_id
     * @param string $provided_slug
     * @param bool $include_site_url
     * @return string
     */
    private function generateUrlWithProvidedSlug($page_id, $provided_slug, $include_site_url = false)
    {
        $result = "";
        $i = 0;
        while (intval($page_id) != 0) {
            $query_builder = \Mtc\Cms\Models\Page::where('id', $page_id);
            if ($i == 0) {
                $query_builder = $query_builder->where(function ($query) {
                    $query->where('type', 'default')
                        ->orWhere('type', 'history');
                });
            }
            $page_data = $query_builder->first(['frontpage', 'type', 'slug', 'sub_id', 'published']);

            if ($page_data === null) {
                $page_id = 0;
                continue;
            }

            // Use overridden slug for the leaf page only
            $slug_to_use = ($i == 0 ? $provided_slug : $page_data->slug);

            if ($page_data->frontpage != 1 && !($slug_to_use == "^" && $i != 0)) {
                $result = $slug_to_use . ($result == "" ? "" : "/") . $result;
            }

            $page_id = $page_data->sub_id;
            ++$i;
        }

        $result = "/" . $result;

        if ($include_site_url == true) {
            $result = SITE_URL . $result;
        }
        return $result;
    }

    /**
     * ContentManager::validatePagedata(array $pagedata_request[])
     *
     * Validates passed pagedata
     *
     * @param array $pagedata_request[], array $files[]
     * @return mixed (true on success, array with error and description on failure and temp_data)
     */
    public function validatePagedata($pagedata_request, $files, $page) {

        $errors = array();

        foreach ($page->page_lists as $list) {
            foreach ($list['items'] as $item) {
                foreach ($item['data'] as $data) {

                    if (!empty($data['required']) && isset($pagedata_request[$data['id']]['value']) && empty($pagedata_request[$data['id']]['value'])) {
                        $errors['by_data_id'][$data['id']][] = array('page_list_item_data_id' => $data['id'], 'type' => 'required', 'message' => 'This cannot be empty!');
                        $errors['by_field_id']['pagedata\\[' . $data['id'] . '\\]\\[value\\]'][] = array('page_list_item_data_id' => $data['id'], 'type' => 'required', 'message' => 'This cannot be empty!');
                    }

                    if (!empty($data['required']) && !isset($pagedata_request[$data['id']]['value']) && empty($data['value'])) {
                        $errors['by_data_id'][$data['id']][] = array('page_list_item_data_id' => $data['id'], 'type' => 'required', 'message' => 'This cannot be empty!');
                        $errors['by_field_id']['pagedata\\[' . $data['id'] . '\\]\\[value\\]'][] = array('page_list_item_data_id' => $data['id'], 'type' => 'required', 'message' => 'This cannot be empty!');
                    }

                    if (!empty($data['regex'])) {
                        if (!empty($pagedata_request[$data['id']]['value']) && isset($pagedata_request[$data['id']]['value']) && ($match = @preg_match($data['regex'], $pagedata_request[$data['id']]['value'])) == false) {
                            $errors['by_data_id'][$data['id']][] = array('page_list_item_data_id' => $data['id'], 'type' => 'regex', 'message' => 'Invalid data supplied!');
                            $errors['by_field_id']['pagedata\\[' . $data['id'] . '\\]\\[value\\]'][] = array('page_list_item_data_id' => $data['id'], 'type' => 'regex', 'message' => 'Invalid data supplied!');
                        } else if (!empty($data['value']) && !isset($pagedata_request[$data['id']]['value']) && ($match = @preg_match($data['regex'], $data['value'])) == false) {
                            $errors['by_data_id'][$data['id']][] = array('page_list_item_data_id' => $data['id'], 'type' => 'regex', 'message' => 'Invalid data supplied!');
                            $errors['by_field_id']['pagedata\\[' . $data['id'] . '\\]\\[value\\]'][] = array('page_list_item_data_id' => $data['id'], 'type' => 'regex', 'message' => 'Invalid data supplied!');
                        }
                    }
                }
            }
        }

        return $errors;

    }

    /**
     * ContentManager::replacePageAndRelativePageId()
     *
     * Replaces old page and its pagedata id ref with new page id
     *
     * @param int $old_page_id - old page id
     * @param int $new_page_id - new page id
     * @return void
     */
    public function replacePageAndRelativePageId($old_page_id, $new_page_id) {
        Page::replacePageId($old_page_id, $new_page_id);
        $plid_list = PageListItemData::where("page_id", $old_page_id)
            ->get(["id", "page_id"]);
        foreach ($plid_list as $key => $plid) {
            $plid->page_id = $new_page_id;
            $plid->save();
        }

        $pli_list = PageListItem::where("page_id", $old_page_id)
            ->get(["id", "page_id"]);
        foreach ($pli_list as $key => $pli) {
            $pli->page_id = $new_page_id;
            $pli->save();
        }

        $pl_list = PageList::where("page_id", $old_page_id)
            ->get(["id", "page_id"]);
        foreach ($pl_list as $key => $pl) {
            $pl->page_id = $new_page_id;
            $pl->save();
        }

        HooksAdapter::do_action(__CLASS__ . '/' . __FUNCTION__, ['old_page_id' => $old_page_id, 'new_page_id' => $new_page_id]);
    }

    /**
     * ContentManager::copyPage
     * Copy a page.
     * @param Page $source_page
     * @return int $new_page_id
     */
    public function copyPage($source_page)
    {
        $with_data = true;
        $copy_child_pages = false;

        $logic = array();

        $pages = array($source_page);

        $logic = array();
        for ($i=0; $i < count($pages); $i++) {
            $lapsg_for_client = 0;
            $logic[] = $this->createPageLapsgLogic(
                $pages[$i],
                $copy_child_pages,
                0,
                $with_data,
                $lapsg_for_client,
                false
            );
        }

        // wrap it in a action
        $logic = array(
            'action' => 'lapsg',
            'logic' => array(
                'pages' => $logic,
            )
        );

        $logic['logic']['pages'][0]['type'] = "temp";
        $logic['logic']['pages'][0]['draft_for_page_id'] = $source_page->draft_for_page_id;
        $logic['logic']['pages'][0]['slug'] = $source_page->slug;

        $generated_pages_on_this_level = $this->processLapsg($logic['logic']['pages'], $source_page->sub_id);
        $new_page_id = $generated_pages_on_this_level[0];

        HooksAdapter::do_action(__CLASS__ . '/' . __FUNCTION__, ['source_page' => $source_page, 'new_page_id' => $new_page_id]);

        return $new_page_id;
    }

    /**
     * ContentManager::processUpdateRequestPageUpdate
     * Process a page and it pagedata update request through validation, non-defined field generation, update etc.
     *
     * @param mixed[] $request - originating from $_post
     * @param mixed[] $files - originating from $_files
     * @param Object Page &$page - page object reference
     * @param bool $force - whether or not to ignore pagedata validation
     * @return mixed[] $response
     */
    public function processUpdateRequestPageUpdate($request, $files, &$page, $force = false) {

        if (isset($request['title']) && empty(trim($request['title'])) || !isset($request['title']) && empty(trim($page->title))) {
            return array(
                'response' => 1,
                'response description'              => "Title cannot be empty!",
                'response_message'                  => "Title cannot be empty!",
                'error_code'                        => 1, // 0 is only good
                'all_errors_sorted_by_field_id'     => array(
                    'data\\[page\\]\\[title\\]'         => array("Title cannot be empty!")
                ),
                'pagedata_errors_sorted_by_data_id' => array()
            );
        }

        if (empty($request['pagedata'])) {
            $request['pagedata'] = array();
        }

        $pagedata_errors = $this->validatePagedata($request['pagedata'], $files, $page);

        if (!empty($pagedata_errors) && $force == false) {
            return array(
                'response'                          => 1,
                'response description'              => "Invalid data",
                'response_message'                  => 'Content field data incorrect!',
                'error_code'                        => 2, // 0 is only good
                'all_errors_sorted_by_field_id'     => $pagedata_errors['by_field_id'],
                'pagedata_errors_sorted_by_data_id' => $pagedata_errors['by_data_id']
            );
        }

        $old_page_slug = $page->slug;
        $old_page_published = $page->published;
        $old_page_url = $page->get_url(false);

        if (isset($request['sub_id'])) {
            $page->sub_id = $request['sub_id'];
            // sub id
        }

        if (isset($request['title'])) {
            $page->title = trim($request['title']);
        }

        if ( isset( $request['type'] ) ) {
            $page->type = $request['type'];
        }

        if (empty($request['save_for_preview'])) {
            if (isset($request['slug'])
                || empty($page->slug)
                || strpos($page->slug, 'enter-title-here') !== false) {

                if (strpos($request['slug'], 'enter-title-here') !== false) {
                    $request['slug'] = null;
                }

                $page->slug = $this->generateSlug(
                    $page->title,
                    (isset($request['slug']) ? $request['slug'] : null),
                    ($page->draft_for_page_id > 0 ? $page->draft_for_page_id : $page->id),
                    $page->sub_id
                );
            }
        }

        // Preflight: we no longer hard-fail on duplicate prospective URL here.
        // The node merge logic below will reconcile with an existing URL node if needed.

        // SETTINGS
        // Check if isset to not change settings when a user enters that doesn't have a privilege to see an input but still can update

        if (isset($request['published'])) {
            $page->published = $request['published'];
        }

        if (isset($request['template'])) {
            $page->template = $request['template'];
        }

        if (isset($request['layout'])) {
            $page->layout = $request['layout'];
        }

        if (isset($request['logic'])) {
            $page->logic = $request['logic'];
        }

        if (isset($request['draft_for_page_id'])) {
            $page->draft_for_page_id = $request['draft_for_page_id'];
        }

        if (isset($request['seo_title'])) {
            $page->seo_title = $request['seo_title'];
        }

        if (isset($request['seo_keywords'])) {
            $page->seo_keywords = $request['seo_keywords'];
        }

        if (isset($request['seo_description'])) {
            $page->seo_description = $request['seo_description'];
        }

        if (isset($request['innav'])) {
            if ( $request['innav'] ) {
                $page->innav = 1;
            } else {
                $page->innav = 0;
            }
        }

        if (isset($request['searchable'])) {
            if ( $request['searchable'] ) {
                $page->searchable = 1;
            } else {
                $page->searchable = 0;
            }
        }

        if (isset($request['redchild'])) {
            if ( $request['redchild'] ) {
                $page->redchild = 1;
            } else {
                $page->redchild = 0;
            }
        }

        if (isset($request['lock'])) {
            if ( $request['lock'] ) {
                $page->lock = 1;
            } else {
                $page->lock = 0;
            }
        }

        if (isset($request['frontpage'])) {
            if ( $request['frontpage'] ) {
                $page->frontpage = 1;
            } else {
                $page->frontpage = 0;
            }
        }

        if (isset($request['contact_page'])) {
            if ( $request['contact_page'] ) {
                $page->contact_page = 1;
            } else {
                $page->contact_page = 0;
            }
        }

        if (isset($request['hide_in_cms'])) {
            if ( $request['hide_in_cms'] ) {
                $page->hide_in_cms = 1;
            } else {
                $page->hide_in_cms = 0;
            }
        }

        if (isset($request['allow_delete'])) {
            if ( $request['allow_delete'] ) {
                $page->allow_delete = 1;
            } else {
                $page->allow_delete = 0;
            }
        }

        if (isset($request['allow_update'])) {
            if ( $request['allow_update'] ) {
                $page->allow_update = 1;
            } else {
                $page->allow_update = 0;
            }
        }

        if (isset($request['listing_container'])) {
            if ($request['listing_container']) {
                $page->listing_container = 1;
            } else {
                $page->listing_container = 0;
            }
        }

        if (isset($request['data1'])) {
            $page->data1 = $request['data1'];
        }

        if (isset($request['directly_viewable'])) {
            if ($request['directly_viewable']) {
                $page->directly_viewable = 1;
            } else {
                $page->directly_viewable = 0;
            }
        }

        if (isset($request['review_status'])) {
            $page->review_status = $request['review_status'];
        }

        if (isset($request['history_lock'])) {
            if ($request['history_lock']) {
                $page->history_lock = 1;
            } else {
                $page->history_lock = 0;
            }
        }

        if (isset($request['noindex'])) {
            if ($request['noindex']) {
                $page->noindex = 1;
            } else {
                $page->noindex = 0;
            }
        }

        if (isset($request['ajax'])) {
            if ($request['ajax']) {
                $page->ajax = 1;
            } else {
                $page->ajax = 0;
            }
        }

        if (isset($request['version'])) {
            $page->version = $request['version'];
        }

        if (isset($request['author_name'])) {
            $page->author_name = $request['author_name'];
        }

        if (isset($request['author_id'])) {
            $page->author_admin_user_id = $request['author_id'];
        }

        if (isset($request['comment'])) {
            $page->comment = $request['comment'];
        }

        // Process Posted Pagedata
        if (isset($request['pagedata'])) {

            foreach ($request['pagedata'] as $i => $value) {
                $pd = new PageListItemData();
                // The page data being manipulated

                // Check if pagedata exists - also with this we get all the pagedata object loaded up
                if (($pd = PageListItemData::find($i)) != null) {

                    if (!defined("CMS_CONVERT_TEXT_INPUT_TO_HTML_ENTITIES") || defined("CMS_CONVERT_TEXT_INPUT_TO_HTML_ENTITIES") && CMS_CONVERT_TEXT_INPUT_TO_HTML_ENTITIES === true) {
                        $request['pagedata'][$i]['value'] = htmlentities($request['pagedata'][$i]['value'], ENT_COMPAT, 'UTF-8');
                    }

                    $pd->value = $request['pagedata'][$i]['value'];
                    $pd->save();
                }
            }
        } // end of process pagedata

        $page->Save();

        $this->generateSearchTitleAndContent($page->id);

        $history_page_id = 0;

        if ($page->draft_for_page_id > 0 && $request['type'] != "temp" && $page->draft_for_page_id != $page->id) {
            // If versioning is enabled 'save' functionality is just saving page as version
            if (defined("CMS_PAGE_VERSIONING") && CMS_PAGE_VERSIONING === true) {
                $page->type = "history";
                $page->updated = date("Y-m-d H:i:s");
                $page->save();
                $history_page_id = $page->id;
            } else {
                $old_page = new Page($page->draft_for_page_id);
                if (!empty($old_page->id)) {
                    Page::delete($page->draft_for_page_id, false, true, false);

                    $this->replacePageAndRelativePageId($page->id, $page->draft_for_page_id);

                    $page = new Page($page->draft_for_page_id);
                    if ($request['slug'] != $old_page->slug) {
                        $page->slug = $this->generateSlug(
                            $page->title,
                            $request['slug'],
                            $page->id,
                            $page->sub_id
                        );
                    }
                    $page->order = $old_page->order;

                    // If its set as frontpage - remove other frontpages
                    if ($page->frontpage == 1 && $page->type == "default") {
                        $this->resetFrontpages();
                    }

                    $page->save();
                    $this->syncPageNode($page);
                }
            }
        } else {
            // If its set as frontpage - remove other frontpages
            if ($page->frontpage == 1 && $page->type == "default") {
                $this->resetFrontpages();
                $page->Save();

            }
            $this->syncPageNode($page);
            // Event fire for events that might want to do something with the new saved page or it's changed url.
            Event::dispatch(
                __CLASS__ . '/' . __FUNCTION__ . ':before_return_during_not_a_version_save',
                [
                    $old_page_slug,
                    $old_page_published,
                    $old_page_url,
                    $page
                ]
            );
        }

        return array(
            'response' => 2,
            'response description' => 'Page with its pagedata has been updated',
            'history_page_id' => $history_page_id
        );
    }

    /**
     * ContentManager::savePageVersion
     * Process a page and it pagedata update request through validation, non-defined field generation, update etc.
     *
     * @param mixed[] $request - originating from $_post
     * @param mixed[] $files - originating from $_files
     * @param Object Page &$page - page object reference
     * @param bool $force - whether or not to ignore pagedata validation
     * @return mixed[] $response
     */
    public function savePageVersion($request, $files, &$page, $force = false) {

        if (isset($request['title']) && empty($request['title']) || !isset($request['title']) && empty($page->title)) {
            return array(
                'response' => 1,
                'response description'              => "Title cannot be empty!",
                'response_message'                  => "Title cannot be empty!",
                'error_code'                        => 1, // 0 is only good
                'all_errors_sorted_by_field_id'     => array(
                    'data\\[page\\]\\[title\\]'         => array("Title cannot be empty!")
                ),
                'pagedata_errors_sorted_by_data_id' => array()
            );
        }

        if (empty($request['pagedata'])) {
            $request['pagedata'] = array();
        }

        $pagedata_errors = $this->validatePagedata($request['pagedata'], $files, $page);

        if (!empty($pagedata_errors) && $force == false) {
            return array(
                'response'                          => 1,
                'response description'              => "Invalid data",
                'response_message'                  => 'Content field data incorrect!',
                'error_code'                        => 2, // 0 is only good
                'all_errors_sorted_by_field_id'     => $pagedata_errors['by_field_id'],
                'pagedata_errors_sorted_by_data_id' => $pagedata_errors['by_data_id']
            );
        }

        $page->type = "history";

        if (isset($request['sub_id'])) {
            $page->sub_id = $request['sub_id'];
            // sub id
        }

        if (isset($request['title'])) {
            $page->title = $request['title'];
        }

        if (!empty($request['slug']) && $page->slug != $request['slug']) {
            $page->slug = $this->filterSlug($request['slug']);
        } elseif (isset($request['slug']) && $request['slug'] == "" || empty($page->slug)) {
            $page->slug = $this->filterSlug($page->title);
        }

        if (isset($request['published'])) {
            $page->published = $request['published'];
        }

        if (isset($request['template'])) {
            $page->template = $request['template'];
        }

        if (isset($request['layout'])) {
            $page->layout = $request['layout'];
        }

        if (isset($request['logic'])) {
            $page->logic = $request['logic'];
        }

        if (isset($request['draft_for_page_id'])) {
            $page->draft_for_page_id = $request['draft_for_page_id'];
        }

        if (isset($request['seo_title'])) {
            $page->seo_title = $request['seo_title'];
        }

        if (isset($request['seo_keywords'])) {
            $page->seo_keywords = $request['seo_keywords'];
        }

        if (isset($request['seo_description'])) {
            $page->seo_description = $request['seo_description'];
        }

        if (isset($request['innav'])) {
            if ($request['innav']) {
                $page->innav = 1;
            } else {
                $page->innav = 0;
            }
        }

        if (isset($request['searchable'])) {
            if ($request['searchable']) {
                $page->searchable = 1;
            } else {
                $page->searchable = 0;
            }
        }

        if (isset($request['redchild'])) {
            if ($request['redchild']) {
                $page->redchild = 1;
            } else {
                $page->redchild = 0;
            }
        }

        if (isset($request['lock'])) {
            if ($request['lock']) {
                $page->lock = 1;
            } else {
                $page->lock = 0;
            }
        }

        if (isset($request['frontpage'])) {
            if ($request['frontpage']) {
                $page->frontpage = 1;
            } else {
                $page->frontpage = 0;
            }
        }

        if (isset($request['hide_in_cms'])) {
            if ($request['hide_in_cms']) {
                $page->hide_in_cms = 1;
            } else {
                $page->hide_in_cms = 0;
            }
        }

        if (isset($request['allow_delete'])) {
            if ($request['allow_delete']) {
                $page->allow_delete = 1;
            } else {
                $page->allow_delete = 0;
            }
        }

        if (isset($request['allow_update'])) {
            if ($request['allow_update']) {
                $page->allow_update = 1;
            } else {
                $page->allow_update = 0;
            }
        }

        if (isset($request['listing_container'])) {
            if ($request['listing_container']) {
                $page->listing_container = 1;
            } else {
                $page->listing_container = 0;
            }
        }

        if (isset($request['data1'])) {
            $page->data1 = $request['data1'];
        }

        if (isset($request['directly_viewable'])) {
            if ($request['directly_viewable']) {
                $page->directly_viewable = 1;
            } else {
                $page->directly_viewable = 0;
            }
        }

        if (isset($request['review_status'])) {
            $page->review_status = $request['review_status'];
        }

        if (isset($request['history_lock'])) {
            if ($request['history_lock']) {
                $page->history_lock = 1;
            } else {
                $page->history_lock = 0;
            }
        }

        if (isset($request['noindex'])) {
            if ($request['noindex']) {
                $page->noindex = 1;
            } else {
                $page->noindex = 0;
            }
        }

        if (isset($request['ajax'])) {
            if ($request['ajax']) {
                $page->ajax = 1;
            } else {
                $page->ajax = 0;
            }
        }

        if (isset($request['version'])) {
            $page->version = $request['version'];
        }

        if (isset($request['author_name'])) {
            $page->author_name = $request['author_name'];
        }

        if (isset($request['author_id'])) {
            $page->author_admin_user_id = $request['author_id'];
        }

        if (isset($request['comment'])) {
            $page->comment = $request['comment'];
        }

        // Process Posted Pagedata
        if (isset($request['pagedata'])) {
            foreach ($request['pagedata'] as $i => $value) {
                $pd = new PageListItemData();
                // The page data being manipulated
                // Check if pagedata exists - also with this we get all the pagedata object loaded up
                if (($pd = PageListItemData::find($i)) != null) {
                    // now process according to type
                    if (!defined("CMS_CONVERT_TEXT_INPUT_TO_HTML_ENTITIES") || defined("CMS_CONVERT_TEXT_INPUT_TO_HTML_ENTITIES") && CMS_CONVERT_TEXT_INPUT_TO_HTML_ENTITIES === true) {
                        $request['pagedata'][$i]['value'] = htmlentities($request['pagedata'][$i]['value'], ENT_COMPAT, 'UTF-8');
                    }
                    $pd->value = $request['pagedata'][$i]['value'];
                    $pd->save();
                    // End of update process
                }
            }
        } // end of process pagedata

        $page->versions .= " ".$page->id;
        $page->Save();

        $this->generateSearchTitleAndContent($page->id);

        return array(
            'response' => 2,
            'response description' => 'Page with its pagedata has been updated',
            'history_page_id' => $page->id
        );
    }

    /**
     * ContentManager::commitPage
     * Commit page to be current.
     *
     * @param mixed[] $request - originating from $_post
     * @param mixed[] $files - originating from $_files
     * @param Object Page &$page - page object reference
     * @param bool $force - whether or not to ignore pagedata validation
     * @return mixed[] $response
     */
    public function commitPage($page, $comment) {
        if ($page->draft_for_page_id > 0 && $page->type == "temp" && $page->draft_for_page_id != $page->id) {
            $old_page = new Page($page->draft_for_page_id);
            if (empty($old_page->id)) {
                return array(
                    'response' => 1,
                    'response_message' => 'Page couldn\'t be made current because the live page might not exsit! If this persists try reloading or recreating this page if possible. '
                );
            }
            $old_page_url = $old_page->get_url(false);
            Page::delete($page->draft_for_page_id, false, true, false);
            $this->replacePageAndRelativePageId($page->id, $page->draft_for_page_id);
            $page = new Page($page->draft_for_page_id);
            $page->comment = $comment;
            $page->review_status = 0;
            $page->type = "default";
            $page->order = $old_page->order;
            // If its set as frontpage - remove other frontpages
            if ($page->frontpage == 1 && $page->type == "default") {
                $this->resetFrontpages();
            }
            $slug = $this->generateSlug(
                $page->title,
                $page->slug,
                $page->id,
                $page->sub_id
            );
            if ($slug !== $page->slug) {
                $page->slug = $slug;
                // If slug changes here due to a conflict with other pages
                // the last history page nneds to reflect this aswell.
                $history_page_id = VersioningHandler::getPageLastHistoryVersion($page->versions);
                $history_page = new Page($history_page_id);
                $history_page->slug = $slug;
                $history_page->save();
            }
            $page->save();
            $this->generateSearchTitleAndContent($page->id);
            $this->syncPageNode($page);
            // Event fire for events that might want to do something with the newly commmited page or it's changed url.
            Event::dispatch(
                __CLASS__ . '/' . __FUNCTION__ . ':before_return',
                [
                    $old_page->slug,
                    $old_page->published,
                    $old_page_url,
                    $page
                ]
            );

            return array(
                'response' => 2,
                'response description' => 'Page commited!',
                'history_page_id' => $page->id
            );
        }
    }

    /**
     * Generates and saves the search/seo title and description for a page.
     * @param integer $page_id Optional. The page ID of the page you want togenerate the data for
     * @param Page    $page    Optional. The page ID of the page you want togenerate the data for
     * @return void
     */
    public function generateSearchTitleAndContent($page_id = null, $page = null) {
        if (!empty($page_id)) {
            $page = new Page($page_id);
        } elseif (empty($page) && empty($page_id)) {
            return;
        }

        // stemm and add page search_title
        $stemmed_page_search_title = "";
        $page_search_title_tmp = PorterStemmer::multi_stem(clean_page($page->title, true, true));
        if (is_array($page_search_title_tmp)) {
            $stemmed_page_search_title = implode(' ', $page_search_title_tmp);
        }
        $page->search_title = $stemmed_page_search_title;

        // init raw page search content
        $page_search_content = "";

        // add page meta to search content
        $page_search_content .= $page->title . " ";
        $page_search_content .= $page->seo_title . " ";
        $page_search_content .= $page->seo_keywords . " ";
        $page_search_content .= $page->seo_description . " ";

        // Loop and add pagedata content to search content
        for ($i = 0; $i < count($page->page_lists); $i++) {
            for ($j = 0; $j < count($page->page_lists[$i]['items']); $j++) {
                for ($k = 0; $k < count($page->page_lists[$i]['items'][$j]['data']); $k++) {
                    $page_search_content .= $page->page_lists[$i]['items'][$j]['data'][$k]['value'];
                }
            }
        }

        $stemmed_page_search_content = "";
        $page_search_content_tmp = PorterStemmer::multi_stem(clean_page($page_search_content, true, true));
        if (is_array($page_search_content_tmp)) {
            $stemmed_page_search_content = implode(' ', $page_search_content_tmp);
        }

        // add $page_search_content to page object for stemming and stemming
        $page->search_content = $stemmed_page_search_content;
        $page->Save();
    }

    /**
     * Created a trigger/button page (page with type - button) that generates a copy of the passed page
     * @param mixed[] $request
     * @param Page Object $page_to_generate
     * @return bool $success
     */
    public function generateLapsgButton($request, $page_to_generate) {

        $logic_pages = array();

        $logic_pages[] = $this->createPageLapsgLogic(
            $page_to_generate,
            $request['lapsg_recursive'],
            0,
            $request['lapsg_with_content'],
            $request['lapsg_for_client'],
            $request['generate_only_temp_pages'],
            $request['structure_from_template'],
            $request['format_and_simplify_logic']
        );

        $logic = array('action' => 'lapsg', 'logic' => array('pages' => $logic_pages, ));

        $new_page = new Page();
        $new_page->sub_id = $page_to_generate->sub_id;
        $new_page->logic = json_encode(
            $logic,
            ($request['format_and_simplify_logic'] == 1 ? JSON_PRETTY_PRINT : 0)
        );
        $new_page->type = "button";
        $new_page->order = -1;
        $new_page->title = (isset($request['lapsg_button_text']) && !empty($request['lapsg_button_text']) ? $request['lapsg_button_text'] : "+" . $page_to_generate->title);
        $new_page->save();

        $new_page = new Page($new_page->id);
        $new_page->order = -1;
        $new_page->save();

        return true;

    }

    /**
     * ContentManager::checkPageForParentListingContainer()
     * Check if page parent is a listing container
     * @param Object Page $page
     * @return mixed $result - false if parent isn't listing container, the parent id if it is
     */
    public function checkPageForParentListingContainer($page) {
        if ($page->sub_id == 0 ) {
            return false;
        }
        $parent_page = new Page();
        $parent_page->get($page->sub_id);
        if ( $parent_page->listing_container == 1 ) {
            return $parent_page;
        }
    }

    /**
     * ContentManager::processSlugsForPageAndSubs()
     * Generate slugs for page and its subs.
     * @param int $page_id
     * @param int $recursive_limit
     * @param int $recursive_level
     * @param int $dry
     * @return string[] $result
     */
    public function processSlugsForPageAndSubs($page_id = 0, $recursive_limit = PHP_INT_MAX, $recursive_level = 0, $dry = 0) {
        $result = [];
        if ($recursive_limit <= $recursive_level) {
            return $result;
        }
        if ($page_id == 0) {
            $sub_pages = Page::retrieve_subs(0);
        } else {
            $page = new Page();
            $page->get($page_id);

            $new_slug = $this->generateSlug($page->title, $page->slug, $page->id, $page->sub_id);

            // If the new slug is different and
            // either existing slug is not empty and page is anything other then temp (default, history, button) (which if not the case might mean it's a page just created by a trigger)
            // or existing slug is empty and page is default (cases when it's a page being pasted in or generated via logic directly as default)
            if (
                ($new_slug != $page->slug) &&
                (
                    $page->type === "default" ||
                    $page->type === "history" ||
                    $page->type === "temp" && $page->slug !== ""
                )
            ) {
                $result[] = [
                    "message"     => "Page (" . $page->id . ") '" . clean_page($page->title) . "' slug updated '" . $page->slug . "' -> '" . $new_slug . "'",
                    "old_slug"    => $page->slug,
                    "new_slug"    => $new_slug,
                    "page_id"     => $page->id,
                    "page_title"  => $page->title,
                    "page_sub_id" => $page->sub_id,
                ];
                if ($dry == 0) {
                    $page->slug = $new_slug;
                    $page->save();
                    $this->generateSearchTitleAndContent(null, $page);
                }
            }
            $sub_pages = $page->get_subs(PHP_INT_MAX, "order", "ASC", false, true);
        }
        foreach ($sub_pages as $sub_page) {
            $recursive_result = $this->processSlugsForPageAndSubs($sub_page->id, $recursive_limit, $recursive_level++, $dry);
            $result = array_merge($recursive_result, $result);
        }

        return $result;
    }

    /**
     * Creates raw trigger/lapsg/button logic to be added in the logic field or inserting in copy
     * @param Page Object $pageObj
     * @param boolean $recursive
     * @param int $n
     * @param boolean $lapsg_with_content
     * @param boolean $generate_only_temp_pages
     * @param int $structure_from_template
     * @param int $format_and_simplify_logic
     * @param int $keep_slugs
     * @return mixed[][] $result
     */
    public function createPageLapsgLogic(
        $pageObj,
        $recursive,
        $n,
        $lapsg_with_content,
        $lapsg_for_client,
        $generate_only_temp_pages,
        $structure_from_template = 0,
        $format_and_simplify_logic = 0,
        $keep_slugs = 0
    ) {

        if (!is_object($pageObj) && $pageObj === 0) {

            $pagesList = Page::getPageList();

            foreach ($pagesList as $key => $page) {

                return $this->createPageLapsgLogic(
                    $page,
                    $recursive,
                    0,
                    $lapsg_with_content,
                    $lapsg_for_client,
                    $generate_only_temp_pages,
                    $structure_from_template,
                    $format_and_simplify_logic,
                    $keep_slugs
                );

            }

            return array();

        }

        $sub_pages = array();

        if ($recursive && $pageObj->has_subs($pageObj->id, false)) {

            $n++;

            foreach (Page::retrieve_subs($pageObj->id, PHP_INT_MAX, "order", "ASC", false) as $ps) {

                $sub_pages[] = $this->createPageLapsgLogic(
                    $ps,
                    $recursive,
                    $n,
                    $lapsg_with_content,
                    $lapsg_for_client,
                    $generate_only_temp_pages,
                    $structure_from_template,
                    $format_and_simplify_logic,
                    $keep_slugs
                );

            };

        }

        if ($structure_from_template == 0) {
            $pagedata_object = new Pagedata();
            $pagedata_object->Get($pageObj->id, 0, 0, 0, $lapsg_with_content, true);
            $pagedata = $pagedata_object->pagedata;

            if ($format_and_simplify_logic == 1) {
                $pagedata = Pagedata::removeDefaultData($pagedata);
            }
        } else {
            $pagedata = [
                'from_template' => $pageObj->template
            ];
        }

        $result = array(
            'sub_id'                => array('relative', 'parent'),
            'type'                  => $pageObj->type,
            'title'                 => $pageObj->title,
            'order'                 => null,
            'slug'                  => ($keep_slugs == 1 ? $pageObj->slug : ""),
            'search_title'          => "",
            'search_content'        => "",
            'seo_title'             => $pageObj->seo_title,
            'seo_description'       => $pageObj->seo_description,
            'seo_keywords'          => $pageObj->seo_keywords,
            'template'              => $pageObj->template,
            'layout'                => $pageObj->layout,
            'searchable'            => $pageObj->searchable,
            'logic'                 => $pageObj->logic,
            'innav'                 => $pageObj->innav,
            'hide_in_cms'           => $pageObj->hide_in_cms,
            'redchild'              => $pageObj->redchild,
            'frontpage'             => $pageObj->frontpage,
            'lock'                  => $pageObj->lock,
            'listing_container'     => $pageObj->listing_container,
            'draft_for_page_id'     => $pageObj->draft_for_page_id,
            'allow_delete'          => $pageObj->allow_delete,
            'allow_update'          => $pageObj->allow_update,
            'published'             => $pageObj->published,
            'data1'                 => $pageObj->data1,
            'data2'                 => $pageObj->data2,
            'data3'                 => $pageObj->data3,
            'versions'              => $pageObj->versions,
            'author_name'           => $pageObj->author_name,
            'author_admin_user_id'  => $pageObj->author_admin_user_id,
            'directly_viewable'     => $pageObj->directly_viewable,
            'ajax'                  => $pageObj->ajax,
            'noindex'               => $pageObj->noindex,
            'review_status'         => $pageObj->review_status,
            'history_lock'          => $pageObj->history_lock,
            'comment'               => $pageObj->comment,
            'sub_pages'             => $sub_pages,
            'pagedata'              => $pagedata
        );

        if (defined('CMS_MULTI_LANG') && CMS_MULTI_LANG === true) {
            $result['title_json'] = $pageObj->title_json;
            if(CMS_MULTI_LANG_SEO_DETAILS_IN_JSON === true) {
                $result['seo_title_json'] = $pageObj->seo_title_json;
                $result['seo_description_json'] = $pageObj->seo_description_json;
                $result['seo_keywords_json'] = $pageObj->seo_keywords_json;
            }
        }

        $result = $this->clearVersionSpecificInfo($result);

        if ($generate_only_temp_pages && $n == 0) {
            $result['type'] = 'temp';
        }

        if ($lapsg_for_client && $result['type'] != 'button') {
            $result['title']        = 'Enter Title Here';
            $result['allow_delete'] = 1;
            $result['allow_update'] = 1;
            $result['published']    = 0;
        }

        // if its one level down and doesn't have to fit between existing pages
        if ( $n > 1 ) {
            $result['order'] = $pageObj->order;
        }

        if ($format_and_simplify_logic == 1) {
            $result = Page::removeDefaultData($result);
        }

        return $result;

    }

    /**
     * ContentManager::clearVersionSpecificInfo
     * Clear properties that are related to a page version and shouldn't thus be copied
     * @param string[] $page_property_array array with properties of a page
     * @return string[] $page_property_array array with properties of a page
     */
    public function clearVersionSpecificInfo($page_property_array) {
        $page_property_array['comment'] = "";
        $page_property_array['author_name'] = "";
        $page_property_array['author_admin_user_id'] = "";
        $page_property_array['history_lock'] = "";
        $page_property_array['versions'] = "";
        $page_property_array['draft_for_page_id'] = "";
        return $page_property_array;
    }

    public function processLapsg($pages_array, $sub_id = 0, $n = 0) {

        $first_page_ids_on_this_level_added = array();

        for ($i = 0; $i < count($pages_array); $i++) {

            $new_page = new Page();

            $new_page->sub_id = $sub_id;

            // order
            if (isset($pages_array[$i]['order'])) {
                $new_page->order = $pages_array[$i]['order'];
            }

            // type
            if (isset($pages_array[$i]['type']) && !empty($pages_array[$i]['type'])) {
                $new_page->type = $pages_array[$i]['type'];
            }

            // There must be a title
            if (!isset($pages_array[$i]['title']) || empty($pages_array[$i]['title'])) {
                continue;
            }
            $new_page->title = $pages_array[$i]['title'];

            // slug
            if (isset($pages_array[$i]['slug']) && !empty($pages_array[$i]['slug'])) {
                $new_page->slug = $pages_array[$i]['slug'];
            }

            // title_json
            if (isset($pages_array[$i]['title_json'])) {
                $new_page->title_json = $pages_array[$i]['title_json'];
            }

            // seo_title_json
            if (isset($pages_array[$i]['seo_title_json'])) {
                $new_page->seo_title_json = $pages_array[$i]['seo_title_json'];
            }

            // seo_keywords_json
            if (isset($pages_array[$i]['seo_keywords_json'])) {
                $new_page->seo_keywords_json = $pages_array[$i]['seo_keywords_json'];
            }

            // seo_description_json
            if (isset($pages_array[$i]['seo_description_json'])) {
                $new_page->seo_description_json = $pages_array[$i]['seo_description_json'];
            }

            // search_title
            if (isset($pages_array[$i]['search_title'])) {
                $new_page->search_title = $pages_array[$i]['search_title'];
            }

            // search_content
            if (isset($pages_array[$i]['search_content'])) {
                $new_page->search_content = $pages_array[$i]['search_content'];
            }

            // seo_title
            if (isset($pages_array[$i]['seo_title'])) {
                $new_page->seo_title = $pages_array[$i]['seo_title'];
            }

            // seo_keywords
            if (isset($pages_array[$i]['seo_keywords'])) {
                $new_page->seo_keywords = $pages_array[$i]['seo_keywords'];
            }

            // seo_description
            if (isset($pages_array[$i]['seo_description'])) {
                $new_page->seo_description = $pages_array[$i]['seo_description'];
            }

            // seo_description
            if (isset($pages_array[$i]['seo_description'])) {
                $new_page->seo_description = $pages_array[$i]['seo_description'];
            }

            // template
            if (isset($pages_array[$i]['template']) && !empty($pages_array[$i]['template'])) {
                $new_page->template = $pages_array[$i]['template'];
            }

            // layout
            if (isset($pages_array[$i]['layout'])) {
                $new_page->layout = $pages_array[$i]['layout'];
            }

            // searchable
            if (isset($pages_array[$i]['searchable'])) {
                $new_page->searchable = $pages_array[$i]['searchable'];
            }

            // logic
            if (isset($pages_array[$i]['logic'])) {
                $new_page->logic = $pages_array[$i]['logic'];
            }

            // innav
            if (isset($pages_array[$i]['innav'])) {
                $new_page->innav = $pages_array[$i]['innav'];
            }

            // hide_in_cms
            if (isset($pages_array[$i]['hide_in_cms'])) {
                $new_page->hide_in_cms = $pages_array[$i]['hide_in_cms'];
            }

            // redchild
            if (isset($pages_array[$i]['redchild'])) {
                $new_page->redchild = $pages_array[$i]['redchild'];
            }

            // frontpage
            if (isset($pages_array[$i]['frontpage'])) {
                $new_page->frontpage = $pages_array[$i]['frontpage'];
            }

            // lock
            if (isset($pages_array[$i]['lock'])) {
                $new_page->lock = $pages_array[$i]['lock'];
            }

            // listing_container
            if (isset($pages_array[$i]['listing_container'])) {
                $new_page->listing_container = $pages_array[$i]['listing_container'];
            }

            // draft_for_page_id
            if (isset($pages_array[$i]['draft_for_page_id'])) {
                $new_page->draft_for_page_id = $pages_array[$i]['draft_for_page_id'];
            }

            // allow_delete
            if (isset($pages_array[$i]['allow_delete'])) {
                $new_page->allow_delete = $pages_array[$i]['allow_delete'];
            }

            // allow_update
            if (isset($pages_array[$i]['allow_update'])) {
                $new_page->allow_update = $pages_array[$i]['allow_update'];
            }

            // published
            if (isset($pages_array[$i]['published'])) {
                $new_page->published = $pages_array[$i]['published'];
            }

            // data1
            if (isset($pages_array[$i]['data1'])) {
                $new_page->data1 = $pages_array[$i]['data1'];
            }

            // data2
            if (isset($pages_array[$i]['data2'])) {
                $new_page->data2 = $pages_array[$i]['data2'];
            }

            // data3
            if (isset($pages_array[$i]['data3'])) {
                $new_page->data3 = $pages_array[$i]['data3'];
            }

            // ajax
            if (isset($pages_array[$i]['ajax'])) {
                $new_page->ajax = $pages_array[$i]['ajax'];
            }

            // directly_viewable
            if (isset($pages_array[$i]['directly_viewable'])) {
                $new_page->directly_viewable = $pages_array[$i]['directly_viewable'];
            }

            // noindex
            if (isset($pages_array[$i]['noindex'])) {
                $new_page->noindex = $pages_array[$i]['noindex'];
            }

            // history_lock
            if (isset($pages_array[$i]['history_lock'])) {
                $new_page->history_lock = $pages_array[$i]['history_lock'];
            }

            // review_status
            if (isset($pages_array[$i]['review_status'])) {
                $new_page->review_status = $pages_array[$i]['review_status'];
            }

            // versions
            if (isset($pages_array[$i]['versions'])) {
                $new_page->versions = $pages_array[$i]['versions'];
            }

            // author_name
            if (isset($pages_array[$i]['author_name'])) {
                $new_page->author_name = $pages_array[$i]['author_name'];
            }

            // author_admin_user_id
            if (isset($pages_array[$i]['author_admin_user_id'])) {
                $new_page->author_admin_user_id = $pages_array[$i]['author_admin_user_id'];
            }

            // comment
            if (isset($pages_array[$i]['comment'])) {
                $new_page->comment = $pages_array[$i]['comment'];
            }

            $new_page->save();

            // id
            if (!empty($pages_array[$i]['id'])) {
                $this->replacePageAndRelativePageId($new_page->id, $pages_array[$i]['id']);
                $new_page->id = $pages_array[$i]['id'];
            }

            $first_page_ids_on_this_level_added[] = $new_page->id;

            if (isset($pages_array[$i]['pagedata']) && !empty($pages_array[$i]['pagedata'])) {
                $pagedata = new Pagedata();
                $pagedata->get($new_page->id); // retrieve existing
                if (!empty($pages_array[$i]['pagedata']['from_template']) && !empty($this->twig_loader)) {
                    $structure_filename = TemplateHandler::getStructureFilename($pages_array[$i]['pagedata']['from_template']);
                    $structure_request = TemplateHandler::getStructure($structure_filename, $this->twig_loader); // twig loader
                    if ($structure_request['result'] === true) {
                        $pagedata->merge($structure_request['structure']['pagedata']); // merge even tho the pagedata is empty
                    }
                } else {
                    $pagedata->merge($pages_array[$i]['pagedata']); // merge even tho the pagedata is empty
                }
                $pagedata->save();
            }

            if (isset($pages_array[$i]['sub_pages']) && !empty($pages_array[$i]['sub_pages'])) {

                $n++;

                $this->processLapsg($pages_array[$i]['sub_pages'], $new_page->id, $n);

            }

        }

        return $first_page_ids_on_this_level_added;

    }

    public function processCustomPageButton($page) {

        if (($json_decoded_laspg = json_decode($page->logic, true)) !== false) {

            if ($json_decoded_laspg['action'] == 'lapsg') {

                $logic = $json_decoded_laspg['logic'];

                return $this->processLapsg($logic['pages'], $page->sub_id);

            }

        }

    }

    /**
     * ContentManager::processAddPagedata
     * @param mixed[] $request add pagedata request
     * @param object $page page object the pagedata shall be added to
     * Process an add pagedata request.
     */
    public function processAddPagedata($request, $page) {
        if (!empty($request['new_pagedata_name'])) {
            // init new pagedata
            $new_pagedata = new Pagedata();
            $new_pagedata->page_id = $page->id;
            if ($request['new_pagedata_type'] == "select") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => '',
                                        'name' => $request['new_pagedata_name'],
                                        'type' => $request['new_pagedata_type'],
                                        'options' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                    ),
                                )
                            ),
                        )
                    )
                );
            } elseif ($request['new_pagedata_type'] == "datetime") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => "text",
                                        // regex preset for validating dates and datetimes or times d/m/y H:i, d/m/Y or H:i
                                        'regex' => "/^([0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2})|([0-9]{2}\/[0-9]{2}\/[0-9]{4})|([0-9]{2}:[0-9]{2})$/",
                                        'css_class' => "datetime",
                                        'settings' => "d/m/Y H:i"
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif ($request['new_pagedata_type'] == "coordinates") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => "",
                                        'name' => $request['new_pagedata_name'],
                                        'type' => "text",
                                        // regex preset for validating value so that they are real lat long coordinated
                                        // Example -35.565,45.464337
                                        'regex' => "/^-?[0-9]+\.[0-9]+,-?[0-9]+\.[0-9]+$/",
                                        'css_class' => "coordinates", // html class
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif ($request['new_pagedata_type'] == "textarea.simpleTinymce") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'textarea',
                                        'css_class' => 'simpleTinymce'
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif ($request['new_pagedata_type'] == "textarea") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => $request['new_pagedata_type'],
                                        'css_class' => 'tinymce'
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif ($request['new_pagedata_type'] == "fileU5") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'file',
                                        'css_class' => 'fileUploadifive',
                                        'mime' => '^((?!python|zip|compressed|x-msdownload|x-sh|php|perl|inode|x-dosexec)[\s\S])*$'
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif ( $request['new_pagedata_type'] == "fileMU5" ) {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'allow_more'    => 1,
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'file',
                                        'css_class' => 'fileMUploadifive',
                                        'mime' => '^((?!python|zip|compressed|x-msdownload|x-sh|php|perl|inode|x-dosexec)[\s\S])*$'
                                    ),
                                )
                            ),
                        )
                    ),
                );
            } elseif ($request['new_pagedata_type'] == "imageU5") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'image',
                                        'css_class' => 'imageUploadifive',
                                        'data2' => $page->title . " " . $request['new_pagedata_name'],
                                        'mime' => '^image\/(jpeg|bmp|gif|png)$'
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif ( $request['new_pagedata_type'] == "imageMU5" ) {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'allow_more'    => 1,
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'image',
                                        'css_class' => 'imageMUploadifive',
                                        'data2' => $page->title . " " . $request['new_pagedata_name'],
                                        'mime' => '^image\/(jpeg|bmp|gif|png)$'
                                    ),
                                )
                            ),
                        )
                    ),
                );
            } elseif ( $request['new_pagedata_type'] == "gallery" ) {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'type' => 'singleImageItemList',
                        'allow_more'    => 1,
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'image',
                                        'css_class' => 'imageMUploadifive',
                                        'data2' => $page->title . " " . $request['new_pagedata_name'],
                                        'mime' => '^image\/(jpeg|bmp|gif|png)$'
                                    ),
                                )
                            ),
                        )
                    ),
                );
            } elseif ( $request['new_pagedata_type'] == "colorpicker" ) {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name' => $request['new_pagedata_name'],
                                        'type' => 'text',
                                        'regex' => '/^#([A-Fa-f0-9]{6})$/',
                                        'css_class' => 'colorpicker'
                                    ),
                                )
                            ),
                        )
                    ),
                );
            } elseif ($request['new_pagedata_type'] === "tags") {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name' => $request['new_pagedata_name'],
                        'type' => 'cmsPanelTagsList',
                        'allow_more' => 1,
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'name' => $request['new_pagedata_name'],
                                        'value' => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'type' => 'text',
                                        'css_class' => 'cmsPanelTagsInput',
                                    )
                                )
                            )
                        )
                    )
                );
            } elseif (!empty(self::$pagedata_properties[$request['new_pagedata_type']])) {
                $pagedata = self::$pagedata_properties[$request['new_pagedata_type']];

                $pagedata[0]['items'][0]['data'][0]['value'] = '';
                if (! empty($request['new_pagedata_value'])) {
                    $pagedata[0]['items'][0]['data'][0]['value'] = $request['new_pagedata_value'];
                }

                $pagedata[0]['name'] = $request['new_pagedata_name'];
                $pagedata[0]['items'][0]['data'][0]['name'] = $request['new_pagedata_name'];

                $new_pagedata->pagedata = $pagedata;
            } else {
                $new_pagedata->pagedata = array(
                    0 => array(
                        'name'  => $request['new_pagedata_name'],
                        'type'  => $request['new_pagedata_type'],
                        'data1' => $request['data1'] ?? '',
                        'data2' => $request['data2'] ?? '',
                        'data3' => $request['data3'] ?? '',
                        'items' => array(
                            0 => array(
                                'data' => array(
                                    0 => array(
                                        'value'     => (empty($request['new_pagedata_value']) ? '' : $request['new_pagedata_value']),
                                        'name'      => $request['new_pagedata_name'],
                                        'type'      => $request['new_pagedata_type'],
                                        'info'      => (isset($request['info']) ? $request['info'] : ""),
                                        'datatype'  => (isset($request['datatype']) ? $request['datatype'] : ""),
                                        'setup'     => (isset($request['setup']) ? $request['setup'] : ""),
                                        'regex'     => (isset($request['regex']) ? $request['regex'] : ""),
                                        'mime'      => (isset($request['mime']) ? $request['mime'] : ""),
                                        'required'  => (isset($request['required']) ? $request['required'] : ""),
                                        'settings'  => (isset($request['settings']) ? $request['settings'] : ""),
                                        'css_class' => (isset($request['css_class']) ? $request['css_class'] : ""),
                                        'options'   => (isset($request['options']) ? $request['options'] : ""),
                                        'language'  => (isset($request['language']) ? $request['language'] : ""),
                                        'data1'     => (isset($request['data1']) ? $request['data1'] : ""),
                                        'data2'     => (isset($request['data2']) ? $request['data2'] : ""),
                                        'data3'     => (isset($request['data3']) ? $request['data3'] : "")
                                    )
                                )
                            )
                        )
                    )
                );
            }
            return $new_pagedata->Save();
        }
        return false;
    }

    // Validate Page ID
    public function validateId($id) {

        if (!empty($id) && preg_match('/^[0-9]+$/', $id) && Page::exists($id)) {

            return true;

        }

        return false;
    }

    // reset frontpage (remove other frontpages)
    public function resetFrontpages() {
        // There will be multiple frontpage(s) when dealing with multi-site.
        return;
        $db = new CmsPdo();
        $query = "UPDATE `pages` SET `frontpage` = 0 WHERE `frontpage` = 1 AND `type` = 'default'";
        $db->run($query);
    }

    public static function listSelectOptionsOfPageTree($page_tree, $selected_value, $n = 0) {

        $html = "";

        foreach ($page_tree as $key => $value) {

            $indent = "";

            for ($i = 0; $i < $n; $i++) {
                $indent .= "-";
            }

            $html .= "<option " . ($value['id'] == $selected_value ? 'selected' : '') . " value='" . $value['id'] . "'>" . $indent . $value['title'] . "</option>";

            if (count($value['sub']) > 0) {

                $html .= self::listSelectOptionsOfPageTree($value['sub'], $selected_value, $n + 1);

            }

        }

        return $html;

    }

    public function listTemplates() {

        global $twig_loader;
        $result = array();
        $themeDirectories = array();
        $twigDirectories = $twig_loader->getPaths();

        foreach ($twig_loader->getPaths() as $extendPath) {

            $themePath = $extendPath . '/pages/';

            if (is_dir($themePath)) {
                $themeDirectories[] = $extendPath . '/pages/';
            }

        }

        foreach ($themeDirectories as $filesystem_template_path) {

            foreach ($iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator(
            $filesystem_template_path,
            RecursiveDirectoryIterator::SKIP_DOTS
            ),
            RecursiveIteratorIterator::SELF_FIRST
            ) as $item) {
                $sub_path = $iterator->getSubPathName();

                $template_name_from_file = TemplateHandler::readFileAndMatchTemplateName($filesystem_template_path . "/" . $sub_path);
                if ($template_name_from_file === null) {
                    $template_name = $sub_path;
                } else {
                    $template_name = $template_name_from_file;
                }

                $structure_filename = TemplateHandler::getStructureFilename($sub_path);
                $structure_request = TemplateHandler::getStructure($structure_filename, $this->twig_loader);

                $result[$sub_path] = array(
                    'value'         => $sub_path,
                    'file'          => $sub_path,
                    'name'          => $template_name,
                    'has_structure' => $structure_request['result'],
                    'is_disabled'   => false,
                );
            }

        }

        asort($result);
        return $result;
    }

    public function listLayouts() {

        global $twig_loader;

        $result = array();

        foreach ($twig_loader->getPaths() as $extendPath) {

            $themePath = $extendPath . '/layouts/';

            if (is_dir($themePath)) {
                $themeDirectories[] = $extendPath . '/layouts/';
            }

        }

        foreach ($themeDirectories as $filesystem_template_path) {

            foreach ($iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator(
            $filesystem_template_path,
            RecursiveDirectoryIterator::SKIP_DOTS
            ),
            RecursiveIteratorIterator::SELF_FIRST
            ) as $item) {

                $subPath = $iterator->getSubPathName();

                $layout_name = '';

                if (!is_dir($filesystem_template_path . "/" . $subPath)) {
                    if (($f = @fopen($filesystem_template_path . "/" . $subPath, 'r')) !== null) {
                        $first_line = @fgets($f);
                        preg_match("/^{# Layout : ([^(#})]+) #}$/", trim($first_line), $matches);
                        if (!empty($matches[1])) {
                            $layout_name = $matches[1];
                        }
                        fclose($f);
                    }
                }

                if (!empty($layout_name)) {

                    $result[$subPath] = array('value' => $subPath, 'file' => $subPath, 'name' => $layout_name, );

                }

            }

        }

        asort($result);
        return $result;

    }

    /**
     * The function uses the page object to filter page meta data
     * for use within the main render of templates.
     *
     * @param mixed
     * @return void
     */

    public static function setPageSettings($args) {

        $p = &$args[0];
        $page_meta = &$args[1];
        $twig_variables = &$args[2];
        $breadcrumbs = &$args[3];
        // Default meta tag for CMS
        $default_meta = $p->title ." | ". config('app.name');

        // If title isn't defined in SEO pages
        if (empty($page_meta['title']) || $page_meta['title'] == config('app.name')) {
            // If available rewrite title with CMS SEO title otherwise rewrite with default
            if (!empty($p->seo_title)) {
                $page_meta['title'] = $p->seo_title;
            } else {
                $page_meta['title'] = $default_meta;
            }
        }

        /*
         * Set page title
         */
        if (!empty($p->title)) {
            $page_meta['page_title'] = $p->title;
        }

        /*
         * Page title is overridden if "Content title" field is available
         */
        if (!empty($p->pagedata['Content title'][0]['value'])) {
            $page_meta['page_title'] = $p->pagedata['Content title'][0]['value'];
        }

        /*
         * Sub page title is set if "Content sub title" field is available
         */
        if (!empty($p->pagedata['Content sub title'][0]['value'])) {
            $page_meta['page_sub_title'] = $p->pagedata['Content sub title'][0]['value'];
        }

        // If description isn't defined in SEO pages
        if (empty($page_meta['description']) || $page_meta['description'] == config('app.name')) {
            // If available rewrite description with CMS SEO description otherwise rewrite with default
            if (!empty($p->seo_description)) {
                $page_meta['description'] = $p->seo_description;
            } else {
                $page_meta['description'] = $default_meta;
            }
        }

        // Change page title to CMS set variable.
        $page_meta['page_template'] = $p->template;

        // Change page loyout to CMS set variable.
        $page_meta['layout'] = $p->layout;

        /*
         * If breadcrumbs are turned on run through page trail
         * to generate breadcrumbs for use in templates.
         */
        if (defined('BREADCRUMBS') && BREADCRUMBS === true) {
            $breadcrumbs = Navigation::getBreadcrumbTrail($p);
        }

        // Pass the page object through to the view.
        $twig_variables['p'] = $p;

    }

    protected function syncPageNode(Page $page): void
    {
        if (!class_exists(Node::class) || !Schema::hasTable('nodes')) {
            return;
        }

        $url = Page::generate_url($page->id, false);
        if (empty($url) && !empty($page->url)) {
            $url = $page->url;
        }

        if (empty($url)) {
            return;
        }

        $nodeByOwner = Node::withTrashed()
            ->where('nodeable_id', $page->id)
            ->where('nodeable_type', Page::class)
            ->first();

        $nodeByUrl = Node::withTrashed()
            ->where('url', $url)
            ->first();

        if ($nodeByUrl && (!$nodeByOwner || $nodeByUrl->id !== $nodeByOwner->id)) {
            if ($nodeByOwner && $nodeByOwner->id !== $nodeByUrl->id) {
                if (method_exists($nodeByOwner, 'forceDelete')) {
                    $nodeByOwner->forceDelete();
                } else {
                    $nodeByOwner->delete();
                }
            }

            $node = $nodeByUrl;

            if (method_exists($node, 'trashed') && $node->trashed()) {
                $node->restore();
            }
        } else {
            $node = $nodeByOwner;

            if (! $node) {
                $node = new Node();
                $node->nodeable_id = $page->id;
                $node->nodeable_type = Page::class;
            } elseif (method_exists($node, 'trashed') && $node->trashed()) {
                $node->restore();
            }
        }

        $node->nodeable_id = $page->id;
        $node->nodeable_type = Page::class;
        $node->url = $url;
        $node->title = $page->title;
        $node->description = $page->seo_description;
        $node->status = !empty($page->published) ? 'public' : 'not public';
        $node->visibility = !empty($page->published) ? 'public' : 'not public';
        $node->save();
    }

    /**
     * ContentManager::getRequestedLanguage()
     * Parses get requests, session and url data to determine requested language/locale
     * @param mixed[] get
     * @param mixed[] session
     * @param mixed[] server
     * @return string language_code
     */
    public function getRequestedLanguage($get, $session, $server) {
        $language = "";
        // first checking if multi lang is even enabled.
        if ( !defined('CMS_MULTI_LANG') || empty(CMS_MULTI_LANG) ) {
            return $language;
        }
        // then checking if the language switch is GET param based (not recommended)
        if ( CMS_MULTI_URL_PARAM_SWITCH === true ) {
            if ( !empty($get[CMS_MULTI_URL_PARAM_SWITCH_NAME]) && defined('CMS_MULTI_LANG_CODE_'.$get[CMS_MULTI_URL_PARAM_SWITCH_NAME])  ) {
                $language = $get[CMS_MULTI_URL_PARAM_SWITCH_NAME];
            }
        }
        // then checking if the language switch is SESSION based (not recommended)
        if ( CMS_MULTI_SESSION_SWITCH === true ) {
            if ( !empty($session[CMS_MULTI_SESSION_SWITCH_NAME]) && defined('CMS_MULTI_LANG_CODE_'.$session[CMS_MULTI_SESSION_SWITCH_NAME])  ) {
                $language = $session[CMS_MULTI_SESSION_SWITCH_NAME];
            }
        }

        // then checking if the language switch is URL top level domain based
        if ( CMS_MULTI_LANG_SWITCH_MODE === 'URL_TLD' ) {
            preg_match("/\.([a-z]+)$/", $server['HTTP_HOST'], $match);
            $length = strlen($match[1]);
            if ( $length >= CMS_MULTI_LANG_CODE_MIN_LENGTH && $length <= CMS_MULTI_LANG_CODE_MAX_LENGTH && defined('CMS_MULTI_LANG_CODE_'.$match[1]) ) {
                $language = $http_host_parts[$i];
            }
        }

        // then checking if the language switch is URL subdomain based
        if ( CMS_MULTI_LANG_SWITCH_MODE === 'URL_SUBDOMAIN' ) {
            $http_host = str_replace("www.", "", $server['HTTP_HOST']);
            $http_host_parts = explode(".", $http_host);
            for ($i=0; $i < count($http_host_parts); $i++) {
                $length = strlen($http_host_parts[$i]);
                if ( $length >= CMS_MULTI_LANG_CODE_MIN_LENGTH && $length <= CMS_MULTI_LANG_CODE_MAX_LENGTH && defined('CMS_MULTI_LANG_CODE_'.$http_host_parts[$i]) ) {
                    $language = $http_host_parts[$i];
                    break;
                }
            }
        }

        if (CMS_MULTI_LANG_SWITCH_MODE === 'URL_SUBDIR') {
            if (isset($server['REDIRECT_FULLCMS_SLUG_URI'])) {
                $raw_uri = "/".$server["REDIRECT_FULLCMS_SLUG_URI"];
            } elseif (isset($server['REDIRECT_FULLCMS_PAGE'])) {
                $raw_uri = "/".$server['REDIRECT_FULLCMS_PAGE'];
            } else {
                $raw_uri = $server['REQUEST_URI'];
            }
            $language = $this->getValidLanguageCodeFromURLSubDirectory($raw_uri);
        }

        return $language;
    }

    /**
     * Get valid language code from URL sub directory if such exists and is installed.
     * @param string $url
     * @return string @language_code
     */
    protected function getValidLanguageCodeFromURLSubDirectory($url) {
        $language_code = "";
        // Check if there is a language code as a subdirectory (www.domain.com/jp) in the URL that is not shorter
        // than CMS_MULTI_LANG_CODE_MIN_LENGTH and not longer than CMS_MULTI_LANG_CODE_MAX_LENGTH.
        if (
            preg_match(
                "/^\\/([a-z]{".CMS_MULTI_LANG_CODE_MIN_LENGTH.",".CMS_MULTI_LANG_CODE_MAX_LENGTH."})($|\\/)/",
                $url,
                $matches
            )
            && defined('CMS_MULTI_LANG_CODE_' . $matches[1])
        ) {
            $language_code = $matches[1];
        }
        return $language_code;
    }

    /**
     * ContentManager::setLocale()
     * Sets runtime locale and text domain
     * @param string language 2-5 char code
     * @return bool success
     */
    public function setLocaleAndTextDomain($language) {
        // this is why is importan to have $language code fetched from ContentManager::getRequestedLanguage()
        $locale = constant("CMS_MULTI_LANG_CODE_".$language);
        if (setlocale(LC_ALL, $locale) === false){
            return false;
        }
        //bindtextdomain(CMS_TEXT_DOMAIN, SITE_PATH."/".LANGUAGE_LOCALE_PATH);
        //bind_textdomain_codeset(CMS_TEXT_DOMAIN, 'UTF-8');
        //textdomain(CMS_TEXT_DOMAIN);
        return true;
    }

    /**
     * ContentManager::parsePreviewPageRequest()
     * Parse the request to display a preview page
     * @param int $get_cms_preview_page_id the requested page id
     * @param mixed[] $session session variables
     * @param string $server_request_uri $_SERVER['REQUEST_URI'] passed in
     */
    public function parsePreviewPageRequest($get_cms_preview_page_id, $session, $server_request_uri) {
        if (!empty($session['adminId'])) {
            $this->page_load_init_vars['page_id_to_load'] = $get_cms_preview_page_id;
        } else {
            header("HTTP/1.0 302 Found");
            header("Location: /admin/login.php?redir=".urlencode($server_request_uri));
            exit();
        }
    }

    /**
     * ContentManager::parseServerURIsToParts()
     * Parse server variables into uri parts
     * @param mixed[] $server $_SERVER vars
     */
    public function parseServerURIsToParts($server) {
        // check if htaccess has set $_SERVER['REDIRECT_FULLCMS_SLUG_URI']
        // if its has - use it - because its more reliable
        // due to other RewriteRules that could make $_SERVER["REQUEST_URI"] not parsable
        // Like for example - news RewriteRule or item RewriteRule
        if (isset($server['REDIRECT_FULLCMS_SLUG_URI'])) {
            $this->page_load_init_vars['uri_parts'] = $this->URIParts("/".$server["REDIRECT_FULLCMS_SLUG_URI"]);
        } elseif (isset($server['REDIRECT_FULLCMS_PAGE'])) {
            $this->page_load_init_vars['uri_parts'] = $this->URIParts("/".$server['REDIRECT_FULLCMS_PAGE']);
        } else {
            $this->page_load_init_vars['uri_parts'] = $this->URIParts($server['REQUEST_URI']);
        }
    }

    /**
     * ContentManager::checkPageIdAgainstCmsHtmlCachePagesSetting
     * Check page_id against the CMS_HTML_CACHE_PAGES if relevant, if page_id should be cached if it's not now
     * @param bool $cache_html whether or not to cache the html of the current page ($page_id)
     * @param int $page_id the page id which should be checked against CMS_HTML_CACHE_PAGES
     * @return bool $cache_html whether or now to cache the html of the current page ($page_id)
     */
    public function checkPageIdAgainstCmsHtmlCachePagesSetting($cache_html, $page_id) {
        if ($cache_html !== true && defined('CMS_HTML_CACHE_PAGES') && CMS_HTML_CACHE_PAGES !== false) {
            if (CMS_HTML_CACHE_PAGES === true) {
                $cache_html = true;
            } elseif (($pages_to_be_cached = json_decode(CMS_HTML_CACHE_PAGES)) !== false) {
                if (in_array($page_id, $pages_to_be_cached)) {
                    $cache_html = true;
                }
            }
        }
        return $cache_html;
    }

    /**
     * ContentManager::initLoadPage()
     * Initilize prior to loading page using request
     * @param mixed $server server variables
     * @param mixed $get get request variables
     * @param mixed $session session variables
     */
    public function initLoadPage(array $server, array $get, array $session) {

        if ( defined('CMS_MULTI_LANG') && CMS_MULTI_LANG === true ) {
            $this->page_load_init_vars['language'] = $this->getRequestedLanguage($get, $session, $server);
        }
        $is_root_or_indexphp = ($server['REQUEST_URI'] === '/' || $server['REQUEST_URI'] === '/index.php');
        $redirect_set = isset($server['REDIRECT_FULLCMS_PAGE']);

        if (
            (!$redirect_set && $is_root_or_indexphp) ||
            ($redirect_set && $server['REDIRECT_FULLCMS_PAGE'] === "")
        ) {

            if (defined("CMS_PAGE_PREVIEW") && CMS_PAGE_PREVIEW === true && !empty($get['_cms_preview_page_id'])) {
                $this->page_load_init_vars['cache_html'] = false;
                $this->parsePreviewPageRequest($get['_cms_preview_page_id'], $session, $server['REQUEST_URI']);
            } else {
                $this->page_load_init_vars['page_id_to_load'] = $this->getFrontPageId();
            }

        } else {

            // parse the URI
            $this->parseServerURIsToParts($server);

            // page preview functionality
            if (defined("CMS_PAGE_PREVIEW") && CMS_PAGE_PREVIEW === true && !empty($get['_cms_preview_page_id'])) {
                $this->page_load_init_vars['cache_html'] = false;
                $this->parsePreviewPageRequest($get['_cms_preview_page_id'], $session, $server['REQUEST_URI']);
            } elseif (PAGE_OBJECT_CACHE === true) {
                if (isset($server['REDIRECT_FULLCMS_SLUG_URI'])) {
                    $this->page_load_init_vars['page_id_to_load'] = $this->getPageIdFromCacheByUrl("/".$server['REDIRECT_FULLCMS_SLUG_URI']);
                } else {
                    $this->page_load_init_vars['page_id_to_load'] = $this->getPageIdFromCacheByUrl($server['REQUEST_URI']);
                }
            }

            // if thispageid hasn't been fetched yet
            if ($this->page_load_init_vars['page_id_to_load'] == false) {
                if (empty($this->page_load_init_vars['uri_parts']['slugs'])) {
                    $this->page_load_init_vars['page_id_to_load'] = $this->getFrontPageId(); // Empty - get index id as page id
                } else {
                    $wildcard = false;
                    if (!isset($server['REDIRECT_FULLCMS_PAGE'])) {
                        $wildcard = true;
                    }
                    $this->page_load_init_vars['page_id_to_load'] = $this->processURIPartsToPageId($this->page_load_init_vars['uri_parts']['slugs'], $wildcard); // URIparts are good - fetch the id
                    if ($this->page_load_init_vars['page_id_to_load'] === false) {
                        if (defined('CMS_HTML_CACHE_404') && CMS_HTML_CACHE_404 === true) {
                            $this->page_load_init_vars['cache_html'] = true;
                        }
                        $this->page_load_init_vars['not_found_page'] = true;
                        $this->page_load_init_vars['page_id_to_load'] = $this->get_fullcms_404_page();
                    }
                }
            }

        }

        $this->checkPageIdAgainstCmsHtmlCachePagesSetting($this->page_load_init_vars['cache_html'], $this->page_load_init_vars['page_id_to_load']);
        $this->setLocaleAndTextDomain($this->page_load_init_vars['language']);
    }

    /**
     * ContentManager::loadLanguages()
     * Load up all the selectable languages
     * @param Page Object $page page object
     */
    public function loadLanguages($page) {
        $languages = array();
        $language_lister = Language::where('active', 1)->orderBy("order");
        // To allow custom language filtering for custom situations,
        HooksAdapter::do_action(__CLASS__ . '/' . __FUNCTION__, ['language_lister' => $language_lister, 'page' => $page]);
        foreach ($language_lister->get()->toArray() as $language) {
            if ($language['public'] == 0) {
                continue;
            }
            if ($language['default']) {
                $language['code'] = "";
            }
            $language['url'] = Page::generate_url($page->id, false, $language['code']);
            $language['selected'] = false;
            if ($page->language == $language['code']) {
                $language['selected'] = true;
            }
            $languages[] = $language;
        }
        return $languages;
    }

    /**
     * Set twig loader
     * @param Twig Loader Object $twig_loader
     */
    public function setTwigLoader($twig_loader) {
        $this->twig_loader = $twig_loader;
    }

    /**
     * Refresh cached html pages
     *
     * @author Haroldas Latonas <haroldas.latonas@mtcmedia.co.uk>
     * @params string $page Slug of the cached page.
     * @return void
     */
    public static function refreshPageCache($page_name) {
        $file_name = SITE_PATH . THEME_PATH . DIRECTORY_SEPARATOR . $page_name . ".html";
        if ( in_array($page_name,[404,"404"]) && file_exists($file_name) ) {
            unlink($file_name);
        }
    }
}
