<commit_before><?php
/*
 * Plugin Name: EPFL Menus
 * Description: Stitch menus across sites
 * Version:     0.3
 *
 */

namespace EPFL\Menus;

/* Copyright © 2018 École Polytechnique Fédérale de Lausanne, Switzerland */
/* All Rights Reserved, except as stated in the LICENSE file. */
/**
 * The EPFL is a pretty big place, and Wordpress' access control is
 * not up to task to its administrative complexity. The solution we
 * came up with is to apportion pages and posts of large Web sites
 * into as many WordPress instances, living under the same Apache
 * server and URL tree, as there are administrative subdivisions to
 * cater to. This is made transparent to the visitor through a number
 * of tricks and extensions, including this one.
 */


if (! defined( 'ABSPATH' )) {
    die( 'Access denied.' );
}

require_once(ABSPATH . 'wp-admin/includes/class-walker-nav-menu-edit.php');


require_once(__DIR__ . '/lib/model.php');
use \EPFL\Model\TypedPost;

require_once(__DIR__ . '/lib/results.php');
use \EPFL\Results\FindFromAllTrait;

require_once(__DIR__ . '/lib/rest.php');
use \EPFL\REST\REST_API;
use \EPFL\REST\RESTClient;
use \EPFL\REST\RESTClientError;
use \EPFL\REST\RESTRemoteError;
use \EPFL\REST\RESTAPIError;

require_once(__DIR__ . '/lib/admin-controller.php');
use \EPFL\AdminController\TransientError;
use \EPFL\AdminController\CustomPostTypeController;

require_once(__DIR__ . '/lib/pubsub.php');
use \EPFL\Pubsub\PublishController;
use \EPFL\Pubsub\SubscribeController;
use \EPFL\Pubsub\TestWebhookFlowException;

require_once(__DIR__ . '/lib/i18n.php');
use function EPFL\I18N\___;
use function EPFL\I18N\__x;
use function EPFL\I18N\__e;
use function EPFL\I18N\get_current_language;

require_once(__DIR__ . '/lib/this-plugin.php');
use EPFL\ThisPlugin\Asset;

require_once(__DIR__ . '/lib/pod.php');
use EPFL\Pod\Site;

class MenuError extends \Exception {}

class TreeError extends \Exception {}

class TreeLoopError extends TreeError {
    function __construct($ids_array) {
        $msg = implode(" ", array_keys($ids_array));
        parent::__construct($msg);
    }
}

/**
 * A Wordpress-style list of menu objects.
 *
 * An instance represents a list of objects that are typically
 * manipulated by Wordpress and its so-called walkers in order to
 * construct menus. Instances are immutable: all mutating operations
 * return a fresh object where all $item objects (the ones returned
 * by @link as_list) are also fresh.
 */
class MenuItemBag
{
    function __construct ($items, $hide_tree_problems=false) {
        if (! is_array($items)) {
            throw new \Error('Bad argument: ' . var_export($items, true));
        }
        $this->items = array();
        foreach ($items as $item) {
            $this->_MUTATE_add_item($item);
        }
        $this->_MUTATE_validate_and_toposort($hide_tree_problems);
    }

    static function coerce ($what) {
        $thisclass = get_called_class();
        if ($what instanceof $thisclass) {
            return $what;
        } else {
            return new $thisclass($what);
        }
    }

    function copy () {
        $thisclass = get_called_class();
        $copied_items = array();
        foreach ($this->as_list() as $item) {
            $copied_items[] = clone $item;
        }
        return new $thisclass($copied_items);
    }

    function mimic_current ($another_menu_tree) {
        $copy = $this->_copy_attributes($another_menu_tree,
                                        array('classes', 'current'));
        $copy->_MUTATE_fixup_current_attributes_and_classes();
        return $copy;
    }

    /**
     * Enforce the same values on attributes 'current',
     * 'current_item_ancestor' and 'current_item_parent' and 'classes'
     * as WordPress' @link wp_nav_menu function would:
     *
     * - every parent of an item that is 'current' is 'current_item_parent'
     *
     * - every parent or ancestor of an item that is 'current' is
     *   'current_item_ancestor'
     *
     * - a node has class 'menu-item-has-children' if and only if
     *   it has children
     */
    private function _MUTATE_fixup_current_attributes_and_classes() {
        $current_items = array();  // Plural 'coz why not?
        foreach ($this->items as $item) {
            $this->_MUTATE_reset_current_ancestry_status($item);
            if ($this->_is_current($item)) {
                $current_items[$this->_get_id($item)] = $item;
            }
        }

        $current_ancestors = array();
        foreach ($current_items as $item) {
            $current_parent_id = $this->_get_parent_id($item);
            if (! $current_parent_id) continue;
            if (! ($current_parent = $this->items[$current_parent_id])) continue;
            $this->_MUTATE_make_parent_of_current($current_parent);

            $current_ancestors[$current_parent_id] = $current_parent;
        }

        while(count($current_ancestors)) {
            $current_ancestors_next = array();
            foreach ($current_ancestors as $current_ancestor) {
                $current_ancestor_id = $this->_get_parent_id($current_ancestor);
                if (! ($current_ancestor =
                       $this->items[$current_ancestor_id])) continue;
                $current_ancestors_next[$current_ancestor_id] = $current_ancestor;
                $this->_MUTATE_make_ancestor_of_current($current_ancestor);
            }
            $current_ancestors = $current_ancestors_next;
        }

        $all_parents = array();
        foreach ($this->items as $item) {
            if (! ($parent_id = $this->_get_parent_id($item))) continue;
            $all_parents[$parent_id] = 1;
        }

        // Fix up 'menu-item-has-children' class or lack thereof
        foreach ($this->items as $item) {
            if ($classes = $item->classes) {
                $item->classes = array_filter(
                    $classes,
                    function($class) {
                        return $class != 'menu-item-has-children';
                    });
            }
        }
        foreach (array_keys($all_parents) as $parent_id) {
            $this->items[$parent_id]->classes[] = 'menu-item-has-children';
            // wp_nav_menu() caller will eliminate any duplicates
        }
    }

    private function _copy_attributes ($another_menu, $attributes) {
        $thisclass = get_called_class();<commit_msg>Add check for incorrect index<commit_after><?php
/*
 * Plugin Name: EPFL Menus
 * Description: Stitch menus across sites
 * Version:     0.3
 *
 */

namespace EPFL\Menus;

/* Copyright © 2018 École Polytechnique Fédérale de Lausanne, Switzerland */
/* All Rights Reserved, except as stated in the LICENSE file. */
/**
 * The EPFL is a pretty big place, and Wordpress' access control is
 * not up to task to its administrative complexity. The solution we
 * came up with is to apportion pages and posts of large Web sites
 * into as many WordPress instances, living under the same Apache
 * server and URL tree, as there are administrative subdivisions to
 * cater to. This is made transparent to the visitor through a number
 * of tricks and extensions, including this one.
 */


if (! defined( 'ABSPATH' )) {
    die( 'Access denied.' );
}

require_once(ABSPATH . 'wp-admin/includes/class-walker-nav-menu-edit.php');


require_once(__DIR__ . '/lib/model.php');
use \EPFL\Model\TypedPost;

require_once(__DIR__ . '/lib/results.php');
use \EPFL\Results\FindFromAllTrait;

require_once(__DIR__ . '/lib/rest.php');
use \EPFL\REST\REST_API;
use \EPFL\REST\RESTClient;
use \EPFL\REST\RESTClientError;
use \EPFL\REST\RESTRemoteError;
use \EPFL\REST\RESTAPIError;

require_once(__DIR__ . '/lib/admin-controller.php');
use \EPFL\AdminController\TransientError;
use \EPFL\AdminController\CustomPostTypeController;

require_once(__DIR__ . '/lib/pubsub.php');
use \EPFL\Pubsub\PublishController;
use \EPFL\Pubsub\SubscribeController;
use \EPFL\Pubsub\TestWebhookFlowException;

require_once(__DIR__ . '/lib/i18n.php');
use function EPFL\I18N\___;
use function EPFL\I18N\__x;
use function EPFL\I18N\__e;
use function EPFL\I18N\get_current_language;

require_once(__DIR__ . '/lib/this-plugin.php');
use EPFL\ThisPlugin\Asset;

require_once(__DIR__ . '/lib/pod.php');
use EPFL\Pod\Site;

class MenuError extends \Exception {}

class TreeError extends \Exception {}

class TreeLoopError extends TreeError {
    function __construct($ids_array) {
        $msg = implode(" ", array_keys($ids_array));
        parent::__construct($msg);
    }
}

/**
 * A Wordpress-style list of menu objects.
 *
 * An instance represents a list of objects that are typically
 * manipulated by Wordpress and its so-called walkers in order to
 * construct menus. Instances are immutable: all mutating operations
 * return a fresh object where all $item objects (the ones returned
 * by @link as_list) are also fresh.
 */
class MenuItemBag
{
    function __construct ($items, $hide_tree_problems=false) {
        if (! is_array($items)) {
            throw new \Error('Bad argument: ' . var_export($items, true));
        }
        $this->items = array();
        foreach ($items as $item) {
            $this->_MUTATE_add_item($item);
        }
        $this->_MUTATE_validate_and_toposort($hide_tree_problems);
    }

    static function coerce ($what) {
        $thisclass = get_called_class();
        if ($what instanceof $thisclass) {
            return $what;
        } else {
            return new $thisclass($what);
        }
    }

    function copy () {
        $thisclass = get_called_class();
        $copied_items = array();
        foreach ($this->as_list() as $item) {
            $copied_items[] = clone $item;
        }
        return new $thisclass($copied_items);
    }

    function mimic_current ($another_menu_tree) {
        $copy = $this->_copy_attributes($another_menu_tree,
                                        array('classes', 'current'));
        $copy->_MUTATE_fixup_current_attributes_and_classes();
        return $copy;
    }

    /**
     * Enforce the same values on attributes 'current',
     * 'current_item_ancestor' and 'current_item_parent' and 'classes'
     * as WordPress' @link wp_nav_menu function would:
     *
     * - every parent of an item that is 'current' is 'current_item_parent'
     *
     * - every parent or ancestor of an item that is 'current' is
     *   'current_item_ancestor'
     *
     * - a node has class 'menu-item-has-children' if and only if
     *   it has children
     */
    private function _MUTATE_fixup_current_attributes_and_classes() {
        $current_items = array();  // Plural 'coz why not?
        foreach ($this->items as $item) {
            $this->_MUTATE_reset_current_ancestry_status($item);
            if ($this->_is_current($item)) {
                $current_items[$this->_get_id($item)] = $item;
            }
        }

        $current_ancestors = array();
        foreach ($current_items as $item) {
            $current_parent_id = $this->_get_parent_id($item);
            if (! $current_parent_id) continue;
            if (! ($current_parent = $this->items[$current_parent_id])) continue;
            $this->_MUTATE_make_parent_of_current($current_parent);

            $current_ancestors[$current_parent_id] = $current_parent;
        }

        while(count($current_ancestors)) {
            $current_ancestors_next = array();
            foreach ($current_ancestors as $current_ancestor) {
                $current_ancestor_id = $this->_get_parent_id($current_ancestor);
                if(!array_key_exists($current_ancestor_id, $this->items))continue;
                $current_ancestor = $this->items[$current_ancestor_id];
                $current_ancestors_next[$current_ancestor_id] = $current_ancestor;
                $this->_MUTATE_make_ancestor_of_current($current_ancestor);
            }
            $current_ancestors = $current_ancestors_next;
        }

        $all_parents = array();
        foreach ($this->items as $item) {
            if (! ($parent_id = $this->_get_parent_id($item))) continue;
            $all_parents[$parent_id] = 1;
        }

        // Fix up 'menu-item-has-children' class or lack thereof
        foreach ($this->items as $item) {
            if ($classes = $item->classes) {
                $item->classes = array_filter(
                    $classes,
                    function($class) {
                        return $class != 'menu-item-has-children';
                    });
            }
        }
        foreach (array_keys($all_parents) as $parent_id) {
            $this->items[$parent_id]->classes[] = 'menu-item-has-children';
            // wp_nav_menu() caller will eliminate any duplicates
        }
    }

    private function _copy_attributes ($another_menu, $attributes) {
        $thisclass = get_called_class();
{'fraction_non_alphanumeric': 0.1163732668639975, 'fraction_numerical': 0.002648387599314535, 'mean_word_length': 2.6453719477569564, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6883717', 'n_tokens_mistral': 3926, 'n_tokens_neox': 3803, 'n_words': 929}