Completed
Push — master ( 079a66...ee0f97 )
by Andreas
14:21
created

midcom_helper_nav_node::get_subnodes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 0
dl 0
loc 23
ccs 14
cts 14
cp 1
crap 3
rs 9.7998
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.helper.nav
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
/**
10
 * @property array $subnodes
11
 * @package midcom.helper.nav
12
 */
13
class midcom_helper_nav_node extends midcom_helper_nav_item
14
{
15
    private $topic;
16
17
    private $topic_id;
18
19 122
    public function __construct($topic)
20
    {
21 122
        if (is_a($topic, midcom_db_topic::class)) {
22 60
            $this->topic = $topic;
23 60
            $this->topic_id = $topic->id;
24
        } else {
25 73
            $this->topic_id = $topic;
26
        }
27 122
    }
28
29 118
    public function is_readable_by($user_id) : bool
30
    {
31 118
        return (   !$user_id
32 117
                || !$this->guid
33 118
                || midcom::get()->auth->acl->can_do_byguid('midgard:read', $this->guid, midcom_db_topic::class, $user_id));
34
    }
35
36 12
    public function get_subnodes() : array
37
    {
38 12
        if (!isset($this->subnodes)) {
39 12
            if ((int) $this->topic_id == 0) {
40 1
                $this->subnodes = [];
41
            } else {
42
                // Use midgard_collector to get the subnodes
43 11
                $mc = midcom_db_topic::new_collector('up', (int) $this->topic_id);
44 11
                $mc->add_constraint('name', '<>', '');
45 11
                $mc->add_order('metadata.score', 'DESC');
46 11
                $mc->add_order('metadata.created');
47
48
                //we always write all the subnodes to cache and filter for ACLs after the fact
49 11
                midcom::get()->auth->request_sudo('midcom.helper.nav');
50 11
                $subnodes = $mc->get_values('id');
51 11
                midcom::get()->auth->drop_sudo();
52
53 11
                $this->subnodes = $subnodes;
54
            }
55 12
            $this->get_cache()->put_node($this->topic_id, $this->get_data());
56
        }
57
58 12
        return $this->subnodes;
59
    }
60
61
    /**
62
     * @return midcom_helper_nav_leaf[]
63
     */
64 44
    public function get_leaves() : array
65
    {
66 44
        $leaves = $this->get_cache()->get_leaves("{$this->id}-leaves");
67 44
        $from_cache = (false !== $leaves);
68 44
        if (!$from_cache) {
69 40
            debug_add('The leaves have not yet been loaded from the database, we do this now.');
70
71
            // we always write all the leaves to cache and filter for ACLs after the fact
72 40
            midcom::get()->auth->request_sudo('midcom.helper.nav');
73 40
            if ($nap = $this->get_component_nap($this->object)) {
74 40
                $leaves = $nap->get_leaves();
75
            }
76 40
            midcom::get()->auth->drop_sudo();
77
        }
78
79 44
        $result = [];
80 44
        foreach ($leaves as $id => $leaf) {
81 32
            $leaf = new midcom_helper_nav_leaf($this, $leaf, $id, $from_cache);
82 32
            $result[$leaf->id] = $leaf;
83
        }
84 44
        if (!$from_cache) {
85 40
            $this->write_leaves_to_cache($result);
86
        }
87 44
        return $result;
88
    }
89
90
    /**
91
     * Writes the passed leaves to the cache, assigning them to the specified node.
92
     *
93
     * The function will bail out on any critical error. Data inconsistencies will be
94
     * logged and overwritten silently otherwise.
95
     *
96
     * @param midcom_helper_nav_leaf[] $leaves The leaves to store in the cache.
97
     */
98 40
    private function write_leaves_to_cache(array $leaves)
99
    {
100 40
        if (!$this->get_cache()->get_node($this->id)) {
101 27
            debug_add("NAP Caching Engine: Tried to update the topic {$this->name} (#{$this->object->id}) "
102 27
            . 'which was supposed to be in the cache already, but failed to load the object from the database.
103 27
                  Aborting write_to_cache, this is a critical cache inconsistency.', MIDCOM_LOG_WARN);
104 27
            return;
105
        }
106 13
        $cachedata = [];
107 13
        foreach ($leaves as $leaf) {
108 4
            $cachedata[$leaf->id] = $leaf->write_to_cache();
109
        }
110
111 13
        debug_add('Writing ' . count($cachedata) . ' leaves to the cache.');
112 13
        $this->get_cache()->put_leaves("{$this->id}-leaves", $cachedata);
113 13
    }
114
115 122
    protected function prepare_data() : array
116
    {
117 122
        $data = $this->get_cache()->get_node($this->topic_id);
118
119 122
        if (!$data) {
120 113
            midcom::get()->auth->request_sudo('midcom.helper.nav');
121 113
            $data = $this->load_data();
122 109
            midcom::get()->auth->drop_sudo();
123
124 109
            if ($data === null) {
0 ignored issues
show
introduced by
The condition $data === null is always false.
Loading history...
125
                debug_add('We got null for this node, so we do not have any NAP information, returning directly.');
126
                return [];
127
            }
128
129 109
            $this->get_cache()->put_node($data[MIDCOM_NAV_ID], $data);
0 ignored issues
show
Bug introduced by
It seems like $data[MIDCOM_NAV_ID] can also be of type midcom_helper_configuration; however, parameter $key of midcom_services_cache_module_nap::put_node() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

129
            $this->get_cache()->put_node(/** @scrutinizer ignore-type */ $data[MIDCOM_NAV_ID], $data);
Loading history...
130 109
            debug_add("Added the ID {$data[MIDCOM_NAV_ID]} to the cache.");
131
        }
132
133 118
        return $data;
134
    }
135
136 113
    private function load_data()
137
    {
138 113
        if (empty($this->topic)) {
139 66
            $topic = new midcom_core_dbaproxy($this->topic_id, midcom_db_topic::class);
140
        } else {
141 54
            $topic = $this->topic;
142
        }
143
144
        // Retrieve a NAP instance
145 113
        $nap = $this->get_component_nap($topic);
146 109
        if (!$nap) {
0 ignored issues
show
introduced by
$nap is of type midcom_baseclasses_components_navigation, thus it always evaluated to true.
Loading history...
147
            return null;
148
        }
149
150
        // Get the node data and verify this is a node that actually has any relevant NAP
151
        // information. Internal components which don't have
152
        // a NAP interface yet return null here, to be exempt from any NAP processing.
153 109
        $data = $nap->get_node();
154 109
        if ($data === null) {
0 ignored issues
show
introduced by
The condition $data === null is always false.
Loading history...
155
            debug_add("The component '{$topic->component}' did return null for the topic {$topic->id}, indicating no NAP information is available.");
156
            return null;
157
        }
158
159
        // Now complete the node data structure
160 109
        $data[MIDCOM_NAV_NAME] = trim($data[MIDCOM_NAV_NAME]) == '' ? $topic->name : $data[MIDCOM_NAV_NAME];
0 ignored issues
show
Bug introduced by
It seems like $data[MIDCOM_NAV_NAME] can also be of type midcom_helper_configuration; however, parameter $str of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

160
        $data[MIDCOM_NAV_NAME] = trim(/** @scrutinizer ignore-type */ $data[MIDCOM_NAV_NAME]) == '' ? $topic->name : $data[MIDCOM_NAV_NAME];
Loading history...
161 109
        $data[MIDCOM_NAV_URL] = $topic->name . '/';
162 109
        $data[MIDCOM_NAV_GUID] = $topic->guid;
163 109
        $data[MIDCOM_NAV_ID] = $topic->id;
164 109
        $data[MIDCOM_NAV_NODEID] = $topic->up;
165 109
        $data[MIDCOM_NAV_TYPE] = 'node';
166 109
        $data[MIDCOM_NAV_SCORE] = $topic->metadata->score;
167 109
        $data[MIDCOM_NAV_COMPONENT] = $topic->component;
168 109
        $data[MIDCOM_NAV_SORTABLE] = true;
169
170 109
        if (!isset($data[MIDCOM_NAV_CONFIGURATION])) {
171
            $data[MIDCOM_NAV_CONFIGURATION] = null;
172
        }
173
174 109
        if (empty($data[MIDCOM_NAV_NOENTRY])) {
175 109
            $data[MIDCOM_NAV_NOENTRY] = (bool) $topic->metadata->get('navnoentry');
176
        }
177 109
        $data[MIDCOM_NAV_OBJECT] = $topic;
178
179 109
        return $data;
180
    }
181
182
    /**
183
     * @param midcom_db_topic $topic
184
     * @return midcom_baseclasses_components_navigation
185
     */
186 129
    private function get_component_nap($topic)
187
    {
188 129
        $interface = midcom::get()->componentloader->get_interface_class($topic->component);
189 125
        $nap = $interface->get_nap_instance();
190 125
        if (!$nap->set_object($topic)) {
191
            debug_add("Could not set the NAP instance of '{$topic->component}' to the topic {$topic->id}.", MIDCOM_LOG_ERROR);
192
            return null;
193
        }
194 125
        return $nap;
195
    }
196
}
197