Passed
Push — master ( cb2db5...c7daa3 )
by Andreas
18:27
created

midcom_services_permalinks::resolve_permalink()   C

Complexity

Conditions 14
Paths 31

Size

Total Lines 73
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 36.5685

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 38
c 1
b 0
f 0
nc 31
nop 1
dl 0
loc 73
ccs 19
cts 37
cp 0.5135
crap 36.5685
rs 6.2666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package midcom.services
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * Permalink management service.
11
 *
12
 * This service is intended to abstract permalink usage away and replaces the original
13
 * Permalink system integrated into the NAP system.
14
 *
15
 * It is fully decoupled from NAP, so objects, which should be reachable by Permalinks
16
 * no longer need NAP entries. To make the transition to this service transparent, the
17
 * system still includes a NAP GUID reverse-lookup, for backwards compatibility.
18
 *
19
 * The component interface is used to provide a selective way to resolve content objects
20
 * to their URLs, with some heuristics to speed up lookups if they can be mapped to a
21
 * topic.
22
 *
23
 * The current Permalink implementation limits granularity to a GUID level -- permalinks
24
 * map object GUIDs to pages. If you have multiple pages showing the same object, you need
25
 * to decide which one you wish to have as permalink and provide that URL for resolution.
26
 * For the forward lookup, it is allowed to have multiple pages set the same permalink.
27
 *
28
 * Permalinks are always of the form $midcom_root_page_prefix/midcom-permalink-$guid and will
29
 * redirect using a Location HTTP header. Since regular content pages are created, the result
30
 * will be cacheable using the content caching system. This obviously means, that if you
31
 * modify the permalink lookup rules, you have to invalidate all guids that affected by the
32
 * changes. MidCOM will assume that the resolution of Permalinks to real URLs is stable over
33
 * time otherwise. You can also set the no_cache flag during the resolver callback execution
34
 * if you discover that it is a URL you are responsible for but the result should not be
35
 * cached. See there for details.
36
 *
37
 * @see midcom_services_permalinks_resolver
38
 * @package midcom.services
39
 */
40
class midcom_services_permalinks
41
{
42
    /**
43
     * Resolve any GUID into a fully qualified URL which can be relocated
44
     * to. Operates in multiple phases:
45
     *
46
     * 1. Check, whether the GUID is already known by NAP. In case we have the corresponding
47
     *    node/leaf loaded, use its linking information directly.
48
     * 2. Look if we have a topic, in that case, we get the corresponding NAP node and use
49
     *    it to resolve the permalink. If that object is not retrievable, the lookup
50
     *    fails.
51
     * 3. We check whether the object in question has a topic as one of its ancestors. If yes,
52
     *    that topic and its corresponding component is used to lookup the GUID, which might
53
     *    fail.
54
     * 4. As a last resort we have to iterate over all NAP topics to do the resolving.
55
     *
56
     * @param string $guid The GUID to resolve.
57
     * @return string The full HTTP relocation'able URL to the GUID.
58
     */
59 20
    public function resolve_permalink($guid) : ?string
60
    {
61
        // resolves a guid into a fully qualified url, uses some heuristics for that, mainly replaces
62
        // the nap permalink resolver, with the difference that it will be based on the
63
        // components permalink interface code.
64
65 20
        $nav = new midcom_helper_nav();
66
67
        // Step 1: Maybe NAP already knows the topic.
68 20
        if ($napobj = $nav->resolve_guid($guid)) {
69
            return $napobj[MIDCOM_NAV_FULLURL];
70
        }
71
72
        try {
73 20
            $object = midcom::get()->dbfactory->get_object_by_guid($guid);
74
        } catch (midcom_error $e) {
75
            debug_add("Failed to resolve the GUID {$guid}, this is most probably an access denied error.", MIDCOM_LOG_ERROR);
76
            debug_add('Last MidCOM error string: ' . $e->getMessage());
77
            return null;
78
        }
79
80 20
        if (!$object->metadata->is_object_visible_onsite()) {
81
            return null;
82
        }
83
84 20
        if ($object instanceof midcom_db_topic) {
85 13
            if ($napobj = $nav->get_node($object->id)) {
86
                return $napobj[MIDCOM_NAV_FULLURL];
87
            }
88 13
            debug_add("Failed to retrieve the NAP object for topic {$object->id}.", MIDCOM_LOG_INFO);
89 13
            return null;
90
        }
91
92
        // Ok, unfortunately, this is not an immediate topic. We try to traverse
93
        // upwards in the object chain to find a topic.
94 8
        $parent = $object->get_parent();
95
96 8
        while ($parent) {
97
            if ($parent instanceof midcom_db_topic) {
98
                // Verify that this topic is within the current sites tree, if it is not,
99
                // we ignore it.
100
                if ($nav->is_node_in_tree($parent->id, $nav->get_root_node())) {
101
                    if ($object instanceof midcom_db_attachment) {
102
                        return $this->create_attachment_link($object->guid, $object->name);
103
                    }
104
                    if ($return_value = $this->_resolve_permalink_in_topic($parent, $object)) {
105
                        return $return_value;
106
                    }
107
                    break;
108
                }
109
            }
110
            $parent = $parent->get_parent();
111
        }
112
113
        // Bad, this means a full scan,
114
        // We need to try every topic for the GUID.
115 8
        $root_topic = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ROOTTOPIC);
116 8
        if (!empty($root_topic->id)) {
117 8
            $qb = midcom_db_topic::new_query_builder();
118 8
            $qb->add_constraint('name', '<>', '');
119 8
            $qb->add_constraint('up', 'INTREE', $root_topic->id);
120 8
            $topics = $qb->execute();
121
        } else {
122
            $topics = [$root_topic];
123
        }
124 8
        foreach ($topics as $topic) {
125
            if ($result = $this->_resolve_permalink_in_topic($topic, $object)) {
126
                return $result;
127
            }
128
        }
129
130
        // We were unable to find the GUID
131 8
        return null;
132
    }
133
134
    private function _resolve_permalink_in_topic(midcom_db_topic $topic, midcom_core_dbaobject $object) : ?string
135
    {
136
        $component = $topic->component;
137
        if (!midcom::get()->componentloader->is_installed($component)) {
138
            return null;
139
        }
140
        $nav = new midcom_helper_nav();
141
        if (!empty($topic->id)) {
142
            $node = $nav->get_node($topic->id);
143
144
            if (!$node) {
145
                debug_add("Failed to load the NAP information of the topic #{$topic->id}, cannot resolve the permalink here.", MIDCOM_LOG_WARN);
146
                debug_print_r('Passed topic was:', $topic);
147
                return null;
148
            }
149
            $prefix = $node[MIDCOM_NAV_FULLURL];
150
        } else {
151
            $prefix = midcom_connection::get_url('prefix');
152
        }
153
154
        $interface = midcom::get()->componentloader->get_interface_class($component);
155
        if ($interface instanceof midcom_services_permalinks_resolver) {
156
            $result = $interface->resolve_object_link($topic, $object);
157
        } else {
158
            $result = null;
159
            foreach ($nav->get_leaves($topic->id) as $leaf) {
160
                if ($leaf[MIDCOM_NAV_GUID] == $object->guid) {
161
                    $result = $leaf[MIDCOM_NAV_URL];
162
                    break;
163
                }
164
            }
165
        }
166
        if ($result === null) {
167
            return null;
168
        }
169
170
        return "{$prefix}{$result}";
171
    }
172
173
    /**
174
     * Create Permalink URLs from GUIDs. They always point to the live site (given correct system configuration).
175
     *
176
     * @param string $guid The Guid to link to.
177
     */
178 126
    public function create_permalink($guid) : string
179
    {
180 126
        return midcom::get()->config->get('midcom_site_url') . "midcom-permalink-{$guid}";
181
    }
182
183
    /**
184
     * Create Permalink URLs for attachments.
185
     * They always point to the live site (given correct system configuration).
186
     *
187
     * @param string $guid The GUID to link to.
188
     * @param string $filename The attachment's filename
189
     */
190 1
    public function create_attachment_link($guid, $filename) : string
191
    {
192 1
        return midcom::get()->config->get('midcom_site_url') . 'midcom-serveattachmentguid-' . $guid . '/' . urlencode($filename);
193
    }
194
}
195