Issues (806)

lib/midcom/services/metadata.php (3 issues)

Labels
Severity
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 array $_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
    private array $_page_classes = [];
34
35
    /**
36
     * Returns the view metadata of the specified context.
37
     *
38
     * @param int $context_id The context to retrieve the view metadata for, this
39
     *     defaults to the current context.
40
     */
41
    public function get_view_metadata(?int $context_id = null) : ?midcom_helper_metadata
42
    {
43
        $context_id ??= midcom_core_context::get()->id;
44
45
        return $this->_metadata[$context_id] ?? null;
46
    }
47
48
    /**
49
     * Sets the class of the current page for a context
50
     */
51 31
    public function set_page_class(string $page_class)
52
    {
53 31
        $context = midcom_core_context::get();
54
55
        // Append current topic to page class if enabled
56 31
        if (midcom::get()->config->get('page_class_include_component')) {
57 31
            $page_class .= ' ' . str_replace('.', '_', $context->get_key(MIDCOM_CONTEXT_COMPONENT));
58
        }
59
60
        // Append a custom class from topic to page class
61 31
        $topic_class = $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC)->get_parameter('midcom.services.metadata', 'page_class');
62 31
        if (!empty($topic_class)) {
63
            $page_class .= " {$topic_class}";
64
        }
65
66 31
        $this->_page_classes[$context->id] = trim($page_class);
67
    }
68
69
    /**
70
     * Gets the CSS class of the current page of a context
71
     */
72
    public function get_page_class() : string
73
    {
74
        $context = midcom_core_context::get();
75
76
        if (array_key_exists($context->id, $this->_page_classes)) {
77
            return $this->_page_classes[$context->id];
78
        }
79
        return 'default';
80
    }
81
82
    /**
83
     * Get CSS classes for an object. This will include two new CSS classes for the object's class string
84
     * if appropriate:
85
     *
86
     * - unapproved: approvals are enabled for the site but the object is not translated
87
     * - hidden: object is hidden via metadata settings or scheduling
88
     */
89 37
    public function get_object_classes(midcom_core_dbaobject $object, ?string $existing_classes = null) : string
90
    {
91 37
        $css_classes = [];
92 37
        if ($existing_classes !== null) {
93 32
            $css_classes[] = $existing_classes;
94
        }
95
96
        // Approval attributes
97 37
        if (   midcom::get()->config->get('metadata_approval')
98 37
            && !$object->metadata->is_approved()) {
99
            $css_classes[] = 'unapproved';
100
        }
101
102
        // Hiding and scheduling attributes
103 37
        if (   (   !midcom::get()->config->get('show_hidden_objects')
104 37
                || midcom::get()->config->get('metadata_scheduling'))
105 37
            && !$object->metadata->is_visible()) {
106
            $css_classes[] = 'hidden';
107
        }
108
109
        // Folder's class
110 37
        if (   $object instanceof midcom_db_topic
111 37
            && $page_class = $object->get_parameter('midcom.services.metadata', 'page_class')) {
112
            $css_classes[] = $page_class;
113
        }
114
115 37
        return implode(' ', $css_classes);
116
    }
117
118
    /**
119
     * Binds view metadata to a DBA content object
120
     */
121 51
    public function bind_to(midcom_core_dbaobject $object)
122
    {
123 51
        $context = midcom_core_context::get();
124
125 51
        $this->_metadata[$context->id] = midcom_helper_metadata::retrieve($object);
126 51
        if (!$this->_metadata[$context->id]) {
127
            return;
128
        }
129
130
        // Update request metadata if appropriate
131 51
        $request_metadata = $this->get_request_metadata($context);
132 51
        $edited = $this->_metadata[$context->id]->get('revised');
133 51
        if ($edited > $request_metadata['lastmodified']) {
134 24
            $this->set_request_metadata($edited, $request_metadata['permalinkguid']);
0 ignored issues
show
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 null|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

134
            $this->set_request_metadata($edited, /** @scrutinizer ignore-type */ $request_metadata['permalinkguid']);
Loading history...
135
        }
136
    }
137
138
    /**
139
     * Populates appropriate metadata into HTML documents based on metadata information
140
     * available to MidCOM for the request.
141
     */
142
    public function populate_meta_head()
143
    {
144
        // HTML generator information
145
        midcom::get()->head->add_meta_head(['name' => 'generator', 'content' => 'MidCOM']);
146
147
        // If an object has been bound we have more information available
148
        if ($view_metadata = $this->get_view_metadata()) {
149
            foreach (midcom::get()->config->get_array('metadata_head_elements') as $property => $metatag) {
150
                if ($content = $view_metadata->get($property)) {
151
                    // Handle date fields
152
                    if (in_array($property, ['published', 'created', 'revised', 'approved', 'locked'])) {
153
                        $content = gmdate('Y-m-d', (int) $content);
154
                    }
155
156
                    midcom::get()->head->add_meta_head([
157
                        'name' => $metatag,
158
                        'content' => $content,
159
                    ]);
160
                }
161
            }
162
            // TODO: Add support for tags here
163
164
            if (midcom::get()->config->get('metadata_opengraph')) {
165
                $this->_add_opengraph_metadata($view_metadata);
166
            }
167
        }
168
    }
169
170
    private function _add_opengraph_metadata(midcom_helper_metadata $view_metadata)
171
    {
172
        $opengraph_type = $view_metadata->object->get_parameter('midcom.helper.metadata', 'opengraph_type');
173
        if (   $opengraph_type
174
            && $opengraph_type != 'none') {
175
            $request_metadata = $this->get_request_metadata();
176
177
            midcom::get()->head->add_meta_head([
178
                'property' => 'og:type',
179
                'content' => $opengraph_type,
180
            ]);
181
            midcom::get()->head->add_meta_head([
182
                'property' => 'og:title',
183
                'content' => midcom_core_context::get()->get_key(MIDCOM_CONTEXT_PAGETITLE),
184
            ]);
185
            midcom::get()->head->add_meta_head([
186
                'property' => 'og:url',
187
                'content' => $request_metadata['permalink'],
188
            ]);
189
            $opengraph_image = $view_metadata->object->get_parameter('midcom.helper.metadata', 'opengraph_image');
190
            if (mgd_is_guid($opengraph_image)) {
191
                midcom::get()->head->add_meta_head([
192
                    'property' => 'og:image',
193
                    'content' => midcom_db_attachment::get_url($opengraph_image),
0 ignored issues
show
It seems like $opengraph_image can also be of type null; however, parameter $attachment of midcom_db_attachment::get_url() does only seem to accept midcom_db_attachment|midgard_attachment|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

193
                    'content' => midcom_db_attachment::get_url(/** @scrutinizer ignore-type */ $opengraph_image),
Loading history...
194
                ]);
195
            }
196
            midcom::get()->head->add_meta_head([
197
                'property' => 'og:description',
198
                'content' => $view_metadata->get('description'),
199
            ]);
200
        }
201
    }
202
203
    /**
204
     * Return a list of Open Graph Protocol types
205
     *
206
     * @see http://opengraphprotocol.org/
207
     */
208 2
    public function get_opengraph_types() : array
209
    {
210 2
        if (!midcom::get()->config->get('metadata_opengraph')) {
211 2
            return [];
212
        }
213
214
        return [
215
            'none' => 'opengraph type select',
216
            'activity' => 'opengraph activity activity',
217
            'sport' => 'opengraph activity sport',
218
            'bar' => 'opengraph business bar',
219
            'company' => 'opengraph business company',
220
            'hotel' => 'opengraph business hotel',
221
            'restaurant' => 'opengraph business restaurant',
222
            'cause' => 'opengraph group cause',
223
            'sports_league' => 'opengraph group sports_league',
224
            'sports_team' => 'opengraph group sports_team',
225
            'band' => 'opengraph organization band',
226
            'government' => 'opengraph organization government',
227
            'non_profit' => 'opengraph organization non_profit',
228
            'school' => 'opengraph organization school',
229
            'university' => 'opengraph organization university',
230
            'actor' => 'opengraph person actor',
231
            'athlete' => 'opengraph person athlete',
232
            'author' => 'opengraph person author',
233
            'director' => 'opengraph person director',
234
            'musician' => 'opengraph person musician',
235
            'politician' => 'opengraph person politician',
236
            'public_figure' => 'opengraph person public_figure',
237
            'city' => 'opengraph place city',
238
            'country' => 'opengraph place country',
239
            'landmark' => 'opengraph place landmark',
240
            'state_province' => 'opengraph place state_province',
241
            'album' => 'opengraph product album',
242
            'book' => 'opengraph product book',
243
            'drink' => 'opengraph product drink',
244
            'food' => 'opengraph product food',
245
            'game' => 'opengraph product game',
246
            'movie' => 'opengraph product movie',
247
            'product' => 'opengraph product product',
248
            'song' => 'opengraph product song',
249
            'tv_show' => 'opengraph product tv_show',
250
            'article' => 'opengraph website article',
251
            'blog' => 'opengraph website blog',
252
            'website' => 'opengraph website website',
253
        ];
254
    }
255
256
    /**
257
     * Get the default Open Graph Protocol type for an object
258
     */
259 2
    public function get_opengraph_type_default() : string
260
    {
261 2
        if (!midcom::get()->config->get('metadata_opengraph')) {
262 2
            return '';
263
        }
264
265
        $context = midcom_core_context::get();
266
        if (empty($this->_metadata[$context->id])) {
267
            return '';
268
        }
269
        $object = $this->_metadata[$context->id]->object;
270
271
        $component = $context->get_key(MIDCOM_CONTEXT_COMPONENT);
272
        if (!$component) {
273
            return '';
274
        }
275
276
        $interface = midcom::get()->componentloader->get_interface_class($component);
277
        if (!method_exists($interface, 'get_opengraph_default')) {
278
            return '';
279
        }
280
281
        return $interface->get_opengraph_default($object);
282
    }
283
284
    /**
285
     * Set the currently known and required Request Metadata: The last modified timestamp and the permalink GUID.
286
     *
287
     * You may set either of the arguments to null to enforce default usage (based on NAP).
288
     */
289 65
    public function set_request_metadata(int $lastmodified, ?string $permalinkguid)
290
    {
291 65
        $context = midcom_core_context::get();
292
293 65
        $context->set_key(MIDCOM_CONTEXT_LASTMODIFIED, $lastmodified);
294 65
        $context->set_key(MIDCOM_CONTEXT_PERMALINKGUID, $permalinkguid);
295
    }
296
297
    /**
298
     * Get the currently known and required Request Metadata: The last modified timestamp and the permalink GUID.
299
     *
300
     * @param midcom_core_context $context The context from which the request metadata should be retrieved. Omit
301
     *     to use the current context.
302
     * @return Array An array with the two keys 'lastmodified' and 'permalinkguid' containing the
303
     *     values set with the setter pendant. For ease of use, there is also a key 'permalink'
304
     *     which contains a ready-made permalink.
305
     */
306 51
    public function get_request_metadata(?midcom_core_context $context = null) : array
307
    {
308 51
        $context ??= midcom_core_context::get();
309 51
        return [
310 51
            'lastmodified' => $context->get_key(MIDCOM_CONTEXT_LASTMODIFIED),
311 51
            'permalinkguid' => $context->get_key(MIDCOM_CONTEXT_PERMALINKGUID),
312 51
            'permalink' => midcom::get()->permalinks->create_permalink($context->get_key(MIDCOM_CONTEXT_PERMALINKGUID)),
0 ignored issues
show
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

312
            'permalink' => midcom::get()->permalinks->create_permalink(/** @scrutinizer ignore-type */ $context->get_key(MIDCOM_CONTEXT_PERMALINKGUID)),
Loading history...
313 51
        ];
314
    }
315
}
316