flack /
openpsa
| 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
Bug
introduced
by
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
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
Loading history...
|
|||||
| 313 | 51 | ]; |
|||
| 314 | } |
||||
| 315 | } |
||||
| 316 |