Completed
Push — master ( 29a652...160801 )
by Andreas
18:29
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. The metadata
39
     * will be created if this is the first request.
40
     *
41
     * @param int $context_id The context to retrieve the view metadata for, this
42
     *     defaults to the current context.
43
     * @return midcom_helper_metadata
44
     */
45
    function & get_view_metadata($context_id = null)
46
    {
47
        if ($context_id === null) {
48
            $context_id = midcom_core_context::get()->id;
49
        }
50
51
        if (!array_key_exists($context_id, $this->_metadata)) {
52
            $this->_metadata[$context_id] = null;
53
        }
54
55
        return $this->_metadata[$context_id];
56
    }
57
58
    /**
59
     * Sets the class of the current page for a context
60
     *
61
     * @param string $page_class The class that should be used for the page
62
     */
63 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...
64
    {
65 31
        $context = midcom_core_context::get();
66
67
        // Append current topic to page class if enabled
68 31
        if (midcom::get()->config->get('page_class_include_component')) {
69 31
            $page_class .= ' ' . str_replace('.', '_', $context->get_key(MIDCOM_CONTEXT_COMPONENT));
70
        }
71
72
        // Append a custom class from topic to page class
73 31
        $topic_class = $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC)->get_parameter('midcom.services.metadata', 'page_class');
74 31
        if (!empty($topic_class)) {
75
            $page_class .= " {$topic_class}";
76
        }
77
78 31
        $this->_page_classes[$context->id] = trim($page_class);
79 31
    }
80
81
    /**
82
     * Gets the CSS class of the current page of a context
83
     *
84
     * @return string The page class
85
     */
86
    public function get_page_class() : string
87
    {
88
        $context = midcom_core_context::get();
89
90
        if (array_key_exists($context->id, $this->_page_classes)) {
91
            return $this->_page_classes[$context->id];
92
        }
93
        return 'default';
94
    }
95
96
    /**
97
     * Get CSS classes for an object. This will include two new CSS classes for the object's class string
98
     * if appropriate:
99
     *
100
     * - unapproved: approvals are enabled for the site but the object is not translated
101
     * - hidden: object is hidden via metadata settings or scheduling
102
     *
103
     * @param midcom_core_dbaobject $object The DBA class instance to get CSS classes for
104
     * @param string $existing_classes Existing CSS classes to append to
105
     * @return string CSS classes for that object
106
     */
107 37
    public function get_object_classes($object, $existing_classes = null) : string
108
    {
109 37
        $css_classes = [];
110 37
        if ($existing_classes !== null) {
111 32
            $css_classes[] = $existing_classes;
112
        }
113
114
        // Approval attributes
115 37
        if (   midcom::get()->config->get('metadata_approval')
116 37
            && !$object->metadata->is_approved()) {
117
            $css_classes[] = 'unapproved';
118
        }
119
120
        // Hiding and scheduling attributes
121 37
        if (   (   !midcom::get()->config->get('show_hidden_objects')
122 37
                || midcom::get()->config->get('metadata_scheduling'))
123 37
            && !$object->metadata->is_visible()) {
124
            $css_classes[] = 'hidden';
125
        }
126
127
        // Folder's class
128 37
        if (   $object instanceof midcom_db_topic
129 37
            && $page_class = $object->get_parameter('midcom.services.metadata', 'page_class')) {
130
            $css_classes[] = $page_class;
131
        }
132
133 37
        return implode(' ', $css_classes);
134
    }
135
136
    /**
137
     * Binds view metadata to a DBA content object
138
     *
139
     * @param midcom_core_dbaobject $object The DBA class instance to bind to.
140
     */
141 51
    public function bind_to($object)
142
    {
143 51
        $context = midcom_core_context::get();
144
145 51
        $this->_metadata[$context->id] = midcom_helper_metadata::retrieve($object);
146 51
        if (!$this->_metadata[$context->id]) {
147
            return;
148
        }
149
150
        // Update request metadata if appropriate
151 51
        $request_metadata = $this->get_request_metadata($context);
152 51
        $edited = $this->_metadata[$context->id]->get('revised');
153 51
        if ($edited > $request_metadata['lastmodified']) {
154 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

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

346
            'permalink' => midcom::get()->permalinks->create_permalink(/** @scrutinizer ignore-type */ $context->get_key(MIDCOM_CONTEXT_PERMALINKGUID)),
Loading history...
347
        ];
348
    }
349
}
350