<?php
/**
 * This source file is part of the open source project
 * ExpressionEngine (https://expressionengine.com)
 *
 * @link      https://expressionengine.com/
 * @copyright Copyright (c) 2003-2022, Packet Tide, LLC (https://www.packettide.com)
 * @license   https://expressionengine.com/license Licensed under Apache License, Version 2.0
 */

/**
 * Layout Model
 */
class Layout_model extends CI_Model
{
    /**
     * Update Layouts
     *
     * Adds a new tab and all associated fields to all existing layouts
     *
     * @access	public
     * @param	array	Altered tabs and/or fields
     * @param	string	Action to take
     * @param	int		The channel id
     * @return	bool
     */
    public function update_layouts($layout_info, $action, $channel_id = array())
    {
        if (! is_array($channel_id)) {
            $channel_id = array($channel_id);
        }

        $this->db->select('layout_id, field_layout');

        if (count($channel_id) > 0) {
            $this->db->where_in('channel_id', $channel_id);
        }

        $query = $this->db->get('layout_publish');
        $errors = array();

        $valid_actions = array('add_tabs', 'delete_tabs', 'add_fields', 'delete_fields');

        if (! in_array($action, $valid_actions)) {
            return false;
        }

        if ($query->num_rows() > 0) {
            foreach ($query->result() as $row) {
                $layout = unserialize($row->field_layout);

                if ($action == 'add_tabs') {
                    foreach ($layout_info as $tab => $fields) {
                        // Check for proper field name

                        $check_name = (is_array($fields)) ? key($fields) : $fields;
                        if ($this->clean_field($check_name) !== true) {
                            $errors[] = $check_name;

                            continue;
                        }

                        if (array_key_exists($tab, $layout) !== true) {
                            $layout[$tab] = $fields;
                        } else {
                            $layout[$tab] = $layout[$tab] + $fields;
                        }
                    }
                } elseif ($action == 'add_fields') {
                    foreach ($layout_info as $tab => $fields) {
                        $check_name = (is_array($fields)) ? key($fields) : $fields;
                        if ($this->clean_field($check_name) !== true) {
                            $errors[] = $check_name;

                            continue;
                        }

                        if (array_key_exists($tab, $layout) !== true) {
                            $layout[$tab] = $fields;
                        } else {
                            $layout[$tab] = $layout[$tab] + $fields;
                        }
                    }
                } elseif ($action == 'delete_tabs') {
                    foreach ($layout_info as $tab => $fields) {
                        $k_field = (is_array($fields)) ? key($fields) : $fields;

                        if ($action == 'delete_tabs' && array_key_exists($tab, $layout) == true) {
                            unset($layout[$tab]);
                        }

                        foreach ($layout as $existing_tab => $existing_field) {
                            if (isset($layout[$existing_tab][$k_field])) {
                                unset($layout[$existing_tab][$k_field]);
                            }
                        }
                    }

                    // Replaced below w/code originally in member model
                    /*
                    foreach($layout_info AS $tab => $fields)
                    {
                        if (array_key_exists($tab, $layout))
                        {
                            unset($layout[$tab]);
                        }

                        foreach ($fields as $field_name => $settings)
                        {
                            foreach ($layout AS $existing_tab => $existing_field)
                            {
                                if (isset($layout[$existing_tab][$field_name]))
                                {
                                    unset($layout[$existing_tab][$field_name]);
                                }
                            }
                        }
                    }
                    */
                } elseif ($action == 'delete_fields') {
                    //  Note- is an array of field names
                    foreach ($layout_info as $field_name) {
                        foreach ($layout as $existing_tab => $existing_field) {
                            if (isset($layout[$existing_tab][$field_name])) {
                                unset($layout[$existing_tab][$field_name]);
                            }
                        }
                    }
                }

                $data = array('field_layout' => serialize($layout));
                $this->db->where('layout_id', $row->layout_id);
                $this->db->update('layout_publish', $data);
            }
        }

        if ($errors > 0) {
            return $errors;
        }

        return true;
    }

    public function clean_field($name)
    {
        // Check for hinkiness in field names
        if (preg_match('/[^a-z0-9\_\-]/i', $name)) {
            return false;
        } elseif (trim($name) == '') {
            return false;
        }

        return true;
    }

    public function edit_layout_fields($layout_info, $action, $channel_id = array(), $alter_tab = false)
    {
        if (! is_array($channel_id)) {
            $channel_id = array($channel_id);
        }

        $this->db->select('layout_id, field_layout');

        if (is_array($channel_id) && count($channel_id) > 0) {
            $this->db->where_in('channel_id', $channel_id);
        }

        $query = $this->db->get('layout_publish');
        $errors = 0;

        $valid_actions = array('show_fields', 'hide_fields', 'edit_fields', 'hide_tab_fields', 'show_tab_fields');

        if (! in_array($action, $valid_actions)) {
            return false;
        }

        if ($query->num_rows() > 0) {
            foreach ($query->result() as $row) {
                $layout = unserialize($row->field_layout);

                if ($action == 'show_fields') {
                    foreach ($layout_info as $field_name) {
                        foreach ($layout as $existing_tab => $existing_field) {
                            if (isset($layout[$existing_tab][$field_name]['visible'])) {
                                $layout[$existing_tab][$field_name]['visible'] = true;
                            }
                        }
                    }
                } elseif ($action == 'hide_fields') {
                    foreach ($layout_info as $field_name) {
                        foreach ($layout as $existing_tab => $existing_field) {
                            if (isset($layout[$existing_tab][$field_name]['visible'])) {
                                $layout[$existing_tab][$field_name]['visible'] = false;
                            }
                        }
                    }
                } elseif ($action == 'edit_fields') {
                    foreach ($layout_info as $field_name => $settings) {
                        foreach ($layout as $existing_tab => $existing_field) {
                            if (isset($layout[$existing_tab][$field_name])) {
                                $layout[$existing_tab][$field_name] = $settings;
                            }
                        }
                    }
                }

                $data = array('field_layout' => serialize($layout));
                $this->db->where('layout_id', $row->layout_id);
                $this->db->update('layout_publish', $data);
            }
        }
    }

    /**
     * Edit one layout group's layout, as opposed to all of the layouts
     *
     * @param Array $layout_info Multidimensional array containing field names as the keys and their settings as the value
     * @param Integer $layout_group_id The layout you want to change settings for
     */
    public function edit_layout_group_fields($layout_info, $layout_group_id)
    {
        if (! ctype_digit($layout_group_id)) {
            return false;
        }

        $query = $this->db->get_where('layout_publish', array('layout_id' => $layout_group_id));

        if ($query->num_rows() > 0) {
            foreach ($query->result() as $row) {
                $layout = unserialize($row->field_layout);

                foreach ($layout_info as $field_name => $settings) {
                    foreach ($layout as $existing_tab => $existing_field) {
                        if (isset($layout[$existing_tab][$field_name])) {
                            $layout[$existing_tab][$field_name] = $settings;
                        }
                    }
                }

                $data = array('field_layout' => serialize($layout));
                $this->db->where('layout_id', $row->layout_id);
                $this->db->update('layout_publish', $data);
            }
        }
    }

    /**
     * Create an array of layout settings for different fields
     * The array is a flat array, NOT organized by tab
     *
     * @param Array $parameters The parameters you want to do WHERE clauses on
     * @return datatype description
     */
    public function get_layout_settings($parameters, $flatten = false)
    {
        if (is_array($parameters) and count($parameters) > 0) {
            $this->_where($parameters);
        } else {
            return false;
        }

        $layout_settings = array();

        $this->db->select('layout_id, field_layout');
        $layouts = $this->db->get('layout_publish');

        if ($layouts->num_rows() > 0) { // More than one row
            foreach ($layouts->result() as $layout) {
                $field_layout = unserialize($layout->field_layout);
                $layout_settings[$layout->layout_id] = $this->_prep_layout_settings($field_layout, $flatten);
            }
        }

        // If there's only one layout, remove the outermost array
        if (count($layout_settings) == 1) {
            $layout_settings = array_shift($layout_settings);
        }

        return $layout_settings;
    }

    /**
     * Sets the WHERE clauses by checking an associative array against an array of valid columns
     *
     * @param Array $parameters Associative array of column names and values for WHERE clauses
     */
    public function _where($parameters)
    {
        $valid_columns = array('layout_id', 'site_id', 'member_group', 'channel_id');

        foreach ($parameters as $column_name => $value) {
            if (in_array($column_name, $valid_columns)) {
                $this->db->where($column_name, $value);
            }
        }
    }

    /**
     * Prep layout settings, appending 'field_id_' to numeric keys
     * Can optionally flatten the layout settings array
     *
     * @param Array $field_layout Unserialized array of layout settings, straight from the database
     * @param Boolean $flatten Whether to flatten the array or not
     * @return datatype description
     */
    public function _prep_layout_settings($field_layout, $flatten = false)
    {
        $layout_settings = array();

        foreach ($field_layout as $layout_tab => $layout_fields) {
            foreach ($layout_fields as $field_key => $field_settings) {
                // Setup $current_settings as the layout settings with the tab name as the key
                $current_settings = & $layout_settings[$layout_tab];

                if ($flatten === true) {
                    // Check to see if the key starts with an underscore, we don't need those
                    if (strncmp($field_key, '_', 1) === 0) {
                        continue;
                    }

                    // If we're flattening the array, ditch the tab information
                    $current_settings = & $layout_settings;
                }

                // If it's numeric, then append 'field_id_'
                if (is_numeric($field_key)) {
                    $current_settings['field_id_' . $field_key] = $field_settings;
                } else {
                    $current_settings[$field_key] = $field_settings;
                }
            }
        }

        return $layout_settings;
    }
}

// EOF
