Passed
Push — master ( b2388c...5da13c )
by Andreas
18:44
created

midcom_services_metadata::bind_to()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 14
ccs 8
cts 9
cp 0.8889
crap 3.0123
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
 * Metadata service.
11
 *
12
 * This service utilizes MidCOM's metadata system to provide meaningful, auto-generated
13
 * meta and link tags into documents. It is also entry point site builders can use to
14
 * retrieve metadata about current page.
15
 *
16
 * @package midcom.services
17
 */
18
class midcom_services_metadata
19
{
20
    /**
21
     * The metadata currently available. This array is indexed by context id; each
22
     * value consists of a metadata object. The metadata objects are created on-demand.
23
     *
24
     * @var midcom_helper_metadata[]
25
     */
26
    private $_metadata = [];
27
28
    /**
29
     * Class of the current page per each context.
30
     * Typically these are the same as the schema name of the current object's Datamanager schema.
31
     * This can be used for changing site styling based on body class="" etc.
32
     *
33
     * @var Array
34
     */
35
    private $_page_classes = [];
36
37
    /**
38
     * Returns the view metadata of the specified context.
39
     *
40
     * @param int $context_id The context to retrieve the view metadata for, this
41
     *     defaults to the current context.
42
     * @return midcom_helper_metadata
43
     */
44
    public function get_view_metadata($context_id = null)
45
    {
46
        if ($context_id === null) {
47
            $context_id = midcom_core_context::get()->id;
48
        }
49
50
        return $this->_metadata[$context_id] ?? null;
51
    }
52
53
    /**
54
     * Sets the class of the current page for a context
55
     *
56
     * @param string $page_class The class that should be used for the page
57
     */
58 31
    function set_page_class($page_class)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
59
    {
60 31
        $context = midcom_core_context::get();
61
62
        // Append current topic to page class if enabled
63 31
        if (midcom::get()->config->get('page_class_include_component')) {
64 31
            $page_class .= ' ' . str_replace('.', '_', $context->get_key(MIDCOM_CONTEXT_COMPONENT));
65
        }
66
67
        // Append a custom class from topic to page class
68 31
        $topic_class = $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC)->get_parameter('midcom.services.metadata', 'page_class');
69 31
        if (!empty($topic_class)) {
70
            $page_class .= " {$topic_class}";
71
        }
72
73 31
        $this->_page_classes[$context->id] = trim($page_class);
74 31
    }
75
76
    /**
77
     * Gets the CSS class of the current page of a context
78
     */
79
    public function get_page_class() : string
80
    {
81
        $context = midcom_core_context::get();
82
83
        if (array_key_exists($context->id, $this->_page_classes)) {
84
            return $this->_page_classes[$context->id];
85
        }
86
        return 'default';
87
    }
88
89
    /**
90
     * Get CSS classes for an object. This will include two new CSS classes for the object's class string
91
     * if appropriate:
92
     *
93
     * - unapproved: approvals are enabled for the site but the object is not translated
94
     * - hidden: object is hidden via metadata settings or scheduling
95
     *
96
     * @param midcom_core_dbaobject $object The DBA class instance to get CSS classes for
97
     * @param string $existing_classes Existing CSS classes to append to
98
     */
99 37
    public function get_object_classes($object, $existing_classes = null) : string
100
    {
101 37
        $css_classes = [];
102 37
        if ($existing_classes !== null) {
103 32
            $css_classes[] = $existing_classes;
104
        }
105
106
        // Approval attributes
107 37
        if (   midcom::get()->config->get('metadata_approval')
108 37
            && !$object->metadata->is_approved()) {
109
            $css_classes[] = 'unapproved';
110
        }
111
112
        // Hiding and scheduling attributes
113 37
        if (   (   !midcom::get()->config->get('show_hidden_objects')
114 37
                || midcom::get()->config->get('metadata_scheduling'))
115 37
            && !$object->metadata->is_visible()) {
116
            $css_classes[] = 'hidden';
117
        }
118
119
        // Folder's class
120 37
        if (   $object instanceof midcom_db_topic
121 37
            && $page_class = $object->get_parameter('midcom.services.metadata', 'page_class')) {
122
            $css_classes[] = $page_class;
123
        }
124
125 37
        return implode(' ', $css_classes);
126
    }
127
128
    /**
129
     * Binds view metadata to a DBA content object
130
     *
131
     * @param midcom_core_dbaobject $object The DBA class instance to bind to.
132
     */
133 51
    public function bind_to($object)
134
    {
135 51
        $context = midcom_core_context::get();
136
137 51
        $this->_metadata[$context->id] = midcom_helper_metadata::retrieve($object);
138 51
        if (!$this->_metadata[$context->id]) {
139
            return;
140
        }
141
142
        // Update request metadata if appropriate
143 51
        $request_metadata = $this->get_request_metadata($context);
144 51
        $edited = $this->_metadata[$context->id]->get('revised');
145 51
        if ($edited > $request_metadata['lastmodified']) {
146 24
            $this->set_request_metadata($edited, $request_metadata['permalinkguid']);
0 ignored issues
show
Bug introduced by
It seems like $request_metadata['permalinkguid'] can also be of type false; however, parameter $permalinkguid of midcom_services_metadata::set_request_metadata() 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

146
            $this->set_request_metadata($edited, /** @scrutinizer ignore-type */ $request_metadata['permalinkguid']);
Loading history...
147
        }
148 51
    }
149
150
    /**
151
     * Populates appropriate metadata into HTML documents based on metadata information
152
     * available to MidCOM for the request.
153
     */
154
    public function populate_meta_head()
155
    {
156
        // HTML generator information
157
        midcom::get()->head->add_meta_head(['name' => 'generator', 'content' => 'MidCOM']);
158
159
        // If an object has been bound we have more information available
160
        if ($view_metadata = $this->get_view_metadata()) {
161
            foreach (midcom::get()->config->get('metadata_head_elements') as $property => $metatag) {
162
                if ($content = $view_metadata->get($property)) {
163
                    // Handle date fields
164
                    switch ($property) {
165
                        case 'published':
166
                        case 'created':
167
                        case 'revised':
168
                        case 'approved':
169
                        case 'locked':
170
                            $content = gmdate('Y-m-d', (int) $content);
171
                            break;
172
                    }
173
174
                    midcom::get()->head->add_meta_head([
175
                        'name' => $metatag,
176
                        'content' => $content,
177
                    ]);
178
                }
179
            }
180
            // TODO: Add support for tags here
181
182
            if (midcom::get()->config->get('metadata_opengraph')) {
183
                $this->_add_opengraph_metadata($view_metadata);
184
            }
185
        }
186
    }
187
188
    private function _add_opengraph_metadata(midcom_helper_metadata $view_metadata)
189
    {
190
        $opengraph_type = $view_metadata->object->get_parameter('midcom.helper.metadata', 'opengraph_type');
191
        if (   $opengraph_type
192
            && $opengraph_type != 'none') {
193
            $request_metadata = $this->get_request_metadata();
194
195
            midcom::get()->head->add_meta_head([
196
                'property' => 'og:type',
197
                'content' => $opengraph_type,
198
            ]);
199
            midcom::get()->head->add_meta_head([
200
                'property' => 'og:title',
201
                'content' => midcom_core_context::get()->get_key(MIDCOM_CONTEXT_PAGETITLE),
202
            ]);
203
            midcom::get()->head->add_meta_head([
204
                'property' => 'og:url',
205
                'content' => $request_metadata['permalink'],
206
            ]);
207
            $opengraph_image = $view_metadata->object->get_parameter('midcom.helper.metadata', 'opengraph_image');
208
            if (mgd_is_guid($opengraph_image)) {
209
                midcom::get()->head->add_meta_head([
210
                    'property' => 'og:image',
211
                    'content' => midcom_db_attachment::get_url($opengraph_image),
212
                ]);
213
            }
214
            midcom::get()->head->add_meta_head([
215
                'property' => 'og:description',
216
                'content' => $view_metadata->get('description'),
217
            ]);
218
        }
219
    }
220
221
    /**
222
     * Return a list of Open Graph Protocol types
223
     *
224
     * @see http://opengraphprotocol.org/
225
     */
226 98
    public function get_opengraph_types() : array
227
    {
228 98
        if (!midcom::get()->config->get('metadata_opengraph')) {
229 98
            return [];
230
        }
231
232
        return [
233
            'none' => 'opengraph type select',
234
            'activity' => 'opengraph activity activity',
235
            'sport' => 'opengraph activity sport',
236
            'bar' => 'opengraph business bar',
237
            'company' => 'opengraph business company',
238
            'hotel' => 'opengraph business hotel',
239
            'restaurant' => 'opengraph business restaurant',
240
            'cause' => 'opengraph group cause',
241
            'sports_league' => 'opengraph group sports_league',
242
            'sports_team' => 'opengraph group sports_team',
243
            'band' => 'opengraph organization band',
244
            'government' => 'opengraph organization government',
245
            'non_profit' => 'opengraph organization non_profit',
246
            'school' => 'opengraph organization school',
247
            'university' => 'opengraph organization university',
248
            'actor' => 'opengraph person actor',
249
            'athlete' => 'opengraph person athlete',
250
            'author' => 'opengraph person author',
251
            'director' => 'opengraph person director',
252
            'musician' => 'opengraph person musician',
253
            'politician' => 'opengraph person politician',
254
            'public_figure' => 'opengraph person public_figure',
255
            'city' => 'opengraph place city',
256
            'country' => 'opengraph place country',
257
            'landmark' => 'opengraph place landmark',
258
            'state_province' => 'opengraph place state_province',
259
            'album' => 'opengraph product album',
260
            'book' => 'opengraph product book',
261
            'drink' => 'opengraph product drink',
262
            'food' => 'opengraph product food',
263
            'game' => 'opengraph product game',
264
            'movie' => 'opengraph product movie',
265
            'product' => 'opengraph product product',
266
            'song' => 'opengraph product song',
267
            'tv_show' => 'opengraph product tv_show',
268
            'article' => 'opengraph website article',
269
            'blog' => 'opengraph website blog',
270
            'website' => 'opengraph website website',
271
        ];
272
    }
273
274
    /**
275
     * Get the default Open Graph Protocol type for an object
276
     */
277 98
    public function get_opengraph_type_default() : string
278
    {
279 98
        if (!midcom::get()->config->get('metadata_opengraph')) {
280 98
            return '';
281
        }
282
283
        $context = midcom_core_context::get();
284
        if (empty($this->_metadata[$context->id])) {
285
            return '';
286
        }
287
        $object = $this->_metadata[$context->id]->object;
288
289
        $component = $context->get_key(MIDCOM_CONTEXT_COMPONENT);
290
        if (!$component) {
291
            return '';
292
        }
293
294
        $interface = midcom::get()->componentloader->get_interface_class($component);
295
        if (!method_exists($interface, 'get_opengraph_default')) {
296
            return '';
297
        }
298
299
        return $interface->get_opengraph_default($object);
300
    }
301
302
    /**
303
     * Set the currently known and required Request Metadata: The last modified timestamp and the permalink GUID.
304
     *
305
     * You may set either of the arguments to null to enforce default usage (based on NAP).
306
     *
307
     * @param int $lastmodified The date of last modification of this request.
308
     * @param string $permalinkguid The GUID used to create a permalink for this request.
309
     */
310 65
    public function set_request_metadata(int $lastmodified, $permalinkguid)
311
    {
312 65
        $context = midcom_core_context::get();
313
314 65
        $context->set_key(MIDCOM_CONTEXT_LASTMODIFIED, $lastmodified);
315 65
        $context->set_key(MIDCOM_CONTEXT_PERMALINKGUID, $permalinkguid);
316 65
    }
317
318
    /**
319
     * Get the currently known and required Request Metadata: The last modified timestamp and the permalink GUID.
320
     *
321
     * @param midcom_core_context $context The context from which the request metadata should be retrieved. Omit
322
     *     to use the current context.
323
     * @return Array An array with the two keys 'lastmodified' and 'permalinkguid' containing the
324
     *     values set with the setter pendant. For ease of use, there is also a key 'permalink'
325
     *     which contains a ready-made permalink.
326
     */
327 51
    public function get_request_metadata(midcom_core_context $context = null) : array
328
    {
329 51
        if ($context === null) {
330
            $context = midcom_core_context::get();
331
        }
332
        return [
333 51
            'lastmodified' => $context->get_key(MIDCOM_CONTEXT_LASTMODIFIED),
334 51
            'permalinkguid' => $context->get_key(MIDCOM_CONTEXT_PERMALINKGUID),
335 51
            'permalink' => midcom::get()->permalinks->create_permalink($context->get_key(MIDCOM_CONTEXT_PERMALINKGUID)),
0 ignored issues
show
Bug introduced by
It seems like $context->get_key(MIDCOM_CONTEXT_PERMALINKGUID) can also be of type false; however, parameter $guid of midcom_services_permalinks::create_permalink() 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

335
            'permalink' => midcom::get()->permalinks->create_permalink(/** @scrutinizer ignore-type */ $context->get_key(MIDCOM_CONTEXT_PERMALINKGUID)),
Loading history...
336
        ];
337
    }
338
}
339