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

create_attachment_link()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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