1<?php
2// src/groupedextensions.php -- HotCRP extensible groups
3// Copyright (c) 2006-2018 Eddie Kohler; see LICENSE.
4
5class GroupedExtensions {
6    private $_subgroups;
7    static private $next_placeholder;
8
9    function _add_json($fj) {
10        if (!isset($fj->name)) {
11            $fj->name = "__" . self::$next_placeholder . "__";
12            ++self::$next_placeholder;
13        }
14        if (!isset($fj->group)) {
15            if (($pos = strrpos($fj->name, "/")) !== false)
16                $fj->group = substr($fj->name, 0, $pos);
17            else
18                $fj->group = $fj->name;
19        }
20        if (!isset($fj->synonym))
21            $fj->synonym = [];
22        else if (is_string($fj->synonym))
23            $fj->synonym = [$fj->synonym];
24        if (!isset($fj->anchorid)
25            && !str_starts_with($fj->name, "__")
26            && ($pos = strpos($fj->name, "/")) !== false) {
27            $x = substr($fj->name, $pos + 1);
28            $fj->anchorid = preg_replace('/\A[^A-Za-z]+|[^A-Za-z0-9_:.]+/', "-", strtolower($x));
29        }
30        $this->_subgroups[] = $fj;
31        return true;
32    }
33    function __construct(Contact $user, $args /* ... */) {
34        self::$next_placeholder = 1;
35        $this->_subgroups = [];
36        foreach (func_get_args() as $i => $arg) {
37            if ($i > 0 && $arg)
38                expand_json_includes_callback($arg, [$this, "_add_json"]);
39        }
40        usort($this->_subgroups, "Conf::xt_priority_compare");
41        $sgs = [];
42        foreach ($this->_subgroups as $gj) {
43            if ($user->conf->xt_allowed($gj, $user)) {
44                if (isset($sgs[$gj->name])) {
45                    $pgj = $sgs[$gj->name];
46                    if (isset($pgj->merge) && $pgj->merge) {
47                        unset($pgj->merge);
48                        $sgs[$gj->name] = object_replace_recursive($gj, $pgj);
49                    }
50                } else
51                    $sgs[$gj->name] = $gj;
52            }
53        }
54        $this->_subgroups = [];
55        foreach ($sgs as $name => $gj) {
56            if (Conf::xt_enabled($gj))
57                $this->_subgroups[$name] = $gj;
58        }
59        uasort($this->_subgroups, function ($aj, $bj) {
60            if ($aj->group !== $bj->group) {
61                if (isset($this->_subgroups[$aj->group]))
62                    $aj = $this->_subgroups[$aj->group];
63                if (isset($this->_subgroups[$bj->group]))
64                    $bj = $this->_subgroups[$bj->group];
65            }
66            return Conf::xt_position_compare($aj, $bj);
67        });
68    }
69    function get($name) {
70        if (isset($this->_subgroups[$name]))
71            return $this->_subgroups[$name];
72        foreach ($this->_subgroups as $gj) {
73            if (in_array($name, $gj->synonym))
74                return $gj;
75        }
76        return null;
77    }
78    function canonical_group($name) {
79        $gj = $this->get($name);
80        return $gj ? $gj->group : false;
81    }
82    function members($name) {
83        if ((string) $name === "")
84            return $this->groups();
85        if (($subgroup = str_ends_with($name, "/*")))
86            $name = substr($name, 0, -2);
87        if (($gj = $this->get($name)))
88            $name = $gj->name;
89        return array_filter($this->_subgroups, function ($gj) use ($name, $subgroup) {
90            return $gj->name === $name ? !$subgroup : $gj->group === $name;
91        });
92    }
93    function all() {
94        return $this->_subgroups;
95    }
96    function groups() {
97        return array_filter($this->_subgroups, function ($gj) {
98            return $gj->name === $gj->group;
99        });
100    }
101    static function render_heading($gj, &$last_title = null,
102                                   $h = 3, $className = null) {
103        if (isset($gj->title)
104            && $gj->title !== $last_title
105            && $gj->group !== $gj->name) {
106            echo '<h', $h;
107            if ($className)
108                echo ' class="', $className, '"';
109            if (isset($gj->anchorid))
110                echo ' id="', htmlspecialchars($gj->anchorid), '"';
111            echo '>', $gj->title, "</h$h>\n";
112            $last_title = $gj->title;
113        }
114    }
115}
116