<commit_before><?php
namespace Taco;

use Taco\Util\Arr as Arr;
use Taco\Util\Collection as Collection;
use Taco\Util\Color as Color;
use Taco\Util\Html as Html;
use Taco\Util\Num as Num;
use Taco\Util\Obj as Obj;
use Taco\Util\Str as Str;

/**
 * Use WordPress posts like a normal model
 * You don't want to use this class directly. Extend it.
 */
class Post extends Base
{
    const ID = 'ID';
    const KEY_CLASS = 'class';
    const KEY_NONCE = 'nonce';
    const URL_SUBMIT = '/wp-content/plugins/taco/post/submit.php';

    public $singular    = null;
    public $plural      = null;
    public $last_error  = null;
    private $_terms     = array();
    
    
    public function getKey()
    {
        return $this->getPostType();
    }


    /**
     * Load a post by ID
     * @param mixed $id String or integer as post ID, or post object
     * @param bool $load_terms
     * @return bool
     */
    public function load($id, $load_terms = true)
    {
        $info = (is_object($id)) ? $id : get_post($id);
        if (!is_object($info)) return false;

        // Handle how WordPress converts special chars out of the DB
        // b/c even when you pass 'raw' as the 3rd partam to get_post,
        // WordPress will still encode the values.
        if (isset($info->post_title) && preg_match('/[&]{1,}/', $info->post_title)) {
            $info->post_title = html_entity_decode($info->post_title);
        }

        $this->_info = (array) $info;

        // meta
        $meta = get_post_meta($this->_info[self::ID]);
        if (Arr::iterable($meta)) {
            foreach ($meta as $k => $v) {
                $this->set($k, current($v));
            }
        }

        // terms
        if (!$load_terms) return true;
        $this->loadTerms();

        return true;
    }
    
    
    /**
     * Load the terms
     * @return bool
     */
    public function loadTerms()
    {
        $taxonomy_keys = $this->getTaxonomyKeys();
        if (!Arr::iterable($taxonomy_keys)) return false;
        
        // TODO Move this to somewhere more efficient
        // Check if this should be an instance of TacoTerm.
        // If not, the object will just be a default WP object from wp_get_post_terms below.
        $taxonomies_subclasses = array();
        $subclasses = Term\Loader::getSubclasses();
        foreach ($subclasses as $subclass) {
            $term_instance = new $subclass;
            $term_instance_taxonomy_key = $term_instance->getKey();
            foreach ($taxonomy_keys as $taxonomy_key) {
                if (array_key_exists($taxonomy_key, $taxonomies_subclasses)) continue;
                if ($term_instance_taxonomy_key !== $taxonomy_key) continue;

                $taxonomies_subclasses[$taxonomy_key] = $subclass;
                break;
            }
        }

        foreach ($taxonomy_keys as $taxonomy_key) {
            $terms = wp_get_post_terms($this->get(self::ID), $taxonomy_key);
            if (!Arr::iterable($terms)) continue;
            
            $terms = array_combine(
                array_map('intval', Collection::pluck($terms, 'term_id')),
                $terms
            );

            // Load Taco\Term if applicable
            if (array_key_exists($taxonomy_key, $taxonomies_subclasses)) {
                $terms = Term\Factory::createMultiple($terms, $taxonomy_key);
            }

            $this->_terms[$taxonomy_key] = $terms;
        }
        
        return true;
    }


    /**
     * Save a post
     * @param bool $exclude_post
     * @return mixed Integer on success: post ID, false on failure (with WP_Error accessible via getLastError)
     */
    public function save($exclude_post = false)
    {
        if (count($this->_info) === 0) return false;

        // set defaults
        $defaults = $this->getDefaults();
        if (count($defaults) > 0) {
            foreach ($defaults as $k => $v) {
                if (!array_key_exists($k, $this->_info)) $this->_info[$k] = $v;
            }
        }

        // separate regular post fields from meta
        $post = array();
        $meta = array();
        $post_fields = static::getCoreFieldKeys();
        $meta_fields = $this->getMetaFieldKeys();
        foreach ($this->_info as $k => $v) {
            if (in_array($k, $post_fields)) $post[$k] = $v;
            elseif (in_array($k, $meta_fields)) $meta[$k] = $v;
        }

        // save post
        $is_update = array_key_exists(self::ID, $post);
        if (!$exclude_post) {
            if ($is_update) wp_update_post($post);
            else {
                // Pass true as the second param to wp_insert_post
                // so that WP won't silently fail if we hit an error
                $id = wp_insert_post($post, true);
                if (!is_int($id)) {
                    $this->last_error = $id;
                    return false;
                }

                $this->_info[self::ID] = $id;
            }

            // Hack to fix ampersand saving in post titles
            // TODO See if there is a better way to do this
            if ($this->_info[self::ID] && array_key_exists('post_title', $post) && preg_match('/[&\']{1,}/', $post['post_title'])) {
                global $wpdb;
                $prepared_sql = $wpdb->prepare(
                    "UPDATE {$wpdb->posts} SET post_title=%s WHERE ID=%d",
                    $post['post_title'],
                    $this->_info[self::ID]
                );
                $wpdb->query($prepared_sql);
            }
        }

        // delete old meta
        if ($is_update) {
            $old_meta = get_post_meta($this->_info[self::ID]);
            if (Arr::iterable($old_meta)) {
                foreach ($old_meta as $old_meta_k => $old_meta_vals) {
                    if (preg_match('/^_/', $old_meta_k)) continue;

                    delete_post_meta($this->_info[self::ID], $old_meta_k);
                }
            }
        }

        // save meta
        if (count($meta) > 0) {
            foreach ($meta as $k => $v) {
                if ($is_update) update_post_meta($this->_info[self::ID], $k, $v);
                else add_post_meta($this->_info[self::ID], $k, $v);
            }
        }

        // save terms<commit_msg>detect link field types in save method<commit_after><?php
namespace Taco;

use Taco\Util\Arr as Arr;
use Taco\Util\Collection as Collection;
use Taco\Util\Color as Color;
use Taco\Util\Html as Html;
use Taco\Util\Num as Num;
use Taco\Util\Obj as Obj;
use Taco\Util\Str as Str;

/**
 * Use WordPress posts like a normal model
 * You don't want to use this class directly. Extend it.
 */
class Post extends Base
{
    const ID = 'ID';
    const KEY_CLASS = 'class';
    const KEY_NONCE = 'nonce';
    const URL_SUBMIT = '/wp-content/plugins/taco/post/submit.php';

    public $singular    = null;
    public $plural      = null;
    public $last_error  = null;
    private $_terms     = array();
    
    
    public function getKey()
    {
        return $this->getPostType();
    }


    /**
     * Load a post by ID
     * @param mixed $id String or integer as post ID, or post object
     * @param bool $load_terms
     * @return bool
     */
    public function load($id, $load_terms = true)
    {
        $info = (is_object($id)) ? $id : get_post($id);
        if (!is_object($info)) return false;

        // Handle how WordPress converts special chars out of the DB
        // b/c even when you pass 'raw' as the 3rd partam to get_post,
        // WordPress will still encode the values.
        if (isset($info->post_title) && preg_match('/[&]{1,}/', $info->post_title)) {
            $info->post_title = html_entity_decode($info->post_title);
        }

        $this->_info = (array) $info;

        // meta
        $meta = get_post_meta($this->_info[self::ID]);
        if (Arr::iterable($meta)) {
            foreach ($meta as $k => $v) {
                $this->set($k, current($v));
            }
        }

        // terms
        if (!$load_terms) return true;
        $this->loadTerms();

        return true;
    }
    
    
    /**
     * Load the terms
     * @return bool
     */
    public function loadTerms()
    {
        $taxonomy_keys = $this->getTaxonomyKeys();
        if (!Arr::iterable($taxonomy_keys)) return false;
        
        // TODO Move this to somewhere more efficient
        // Check if this should be an instance of TacoTerm.
        // If not, the object will just be a default WP object from wp_get_post_terms below.
        $taxonomies_subclasses = array();
        $subclasses = Term\Loader::getSubclasses();
        foreach ($subclasses as $subclass) {
            $term_instance = new $subclass;
            $term_instance_taxonomy_key = $term_instance->getKey();
            foreach ($taxonomy_keys as $taxonomy_key) {
                if (array_key_exists($taxonomy_key, $taxonomies_subclasses)) continue;
                if ($term_instance_taxonomy_key !== $taxonomy_key) continue;

                $taxonomies_subclasses[$taxonomy_key] = $subclass;
                break;
            }
        }

        foreach ($taxonomy_keys as $taxonomy_key) {
            $terms = wp_get_post_terms($this->get(self::ID), $taxonomy_key);
            if (!Arr::iterable($terms)) continue;
            
            $terms = array_combine(
                array_map('intval', Collection::pluck($terms, 'term_id')),
                $terms
            );

            // Load Taco\Term if applicable
            if (array_key_exists($taxonomy_key, $taxonomies_subclasses)) {
                $terms = Term\Factory::createMultiple($terms, $taxonomy_key);
            }

            $this->_terms[$taxonomy_key] = $terms;
        }
        
        return true;
    }


    /**
     * Save a post
     * @param bool $exclude_post
     * @return mixed Integer on success: post ID, false on failure (with WP_Error accessible via getLastError)
     */
    public function save($exclude_post = false)
    {
        if (count($this->_info) === 0) return false;

        // set defaults
        $defaults = $this->getDefaults();
        if (count($defaults) > 0) {
            foreach ($defaults as $k => $v) {
                if (!array_key_exists($k, $this->_info)) $this->_info[$k] = $v;
            }
        }

        // separate regular post fields from meta
        $post = array();
        $meta = array();
        $post_fields = static::getCoreFieldKeys();
        $fields_and_attribs = static::getFields();
        $meta_fields = array_keys($fields_and_attribs);

        foreach ($this->_info as $k => $v) {
            if (in_array($k, $post_fields)) $post[$k] = $v;
            elseif (in_array($k, $meta_fields)) $meta[$k] = $v;
        }

        // save post
        $is_update = array_key_exists(self::ID, $post);
        if (!$exclude_post) {
            if ($is_update) wp_update_post($post);
            else {
                // Pass true as the second param to wp_insert_post
                // so that WP won't silently fail if we hit an error
                $id = wp_insert_post($post, true);
                if (!is_int($id)) {
                    $this->last_error = $id;
                    return false;
                }

                $this->_info[self::ID] = $id;
            }

            // Hack to fix ampersand saving in post titles
            // TODO See if there is a better way to do this
            if ($this->_info[self::ID] && array_key_exists('post_title', $post) && preg_match('/[&\']{1,}/', $post['post_title'])) {
                global $wpdb;
                $prepared_sql = $wpdb->prepare(
                    "UPDATE {$wpdb->posts} SET post_title=%s WHERE ID=%d",
                    $post['post_title'],
                    $this->_info[self::ID]
                );
                $wpdb->query($prepared_sql);
            }
        }

        // delete old meta
        if ($is_update) {
            $old_meta = get_post_meta($this->_info[self::ID]);
            if (Arr::iterable($old_meta)) {
                foreach ($old_meta as $old_meta_k => $old_meta_vals) {
                    if (preg_match('/^_/', $old_meta_k)) continue;

                    delete_post_meta($this->_info[self::ID], $old_meta_k);
                }
            }
        }

        // save meta
        if (count($meta) > 0) {
            foreach ($meta as $k => $v) {
                if (preg_match('/link/', $fields_and_attribs[$k]['type'])) {
                    $v = urldecode($v);
                }
                if ($is_update) update_post_meta($this->_info[self::ID], $k, $v);
                else add_post_meta($this->_info[self::ID], $k, $v);
            }
        }

        // save terms
{'fraction_non_alphanumeric': 0.1328506217535023, 'fraction_numerical': 0.0009444356996694475, 'mean_word_length': 1.7659991292990858, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 76, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5234061', 'n_tokens_mistral': 3890, 'n_tokens_neox': 3731, 'n_words': 954}