1<!DOCTYPE html> 2<html lang="en-US"> 3<head> 4 <meta charset="UTF-8"> 5 <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> 6 <link rel="profile" href="http://microformats.org/profile/specs" /> 7 <link rel="profile" href="http://microformats.org/profile/hatom" /> 8 9 <title>News – Microformats</title> 10<link rel='dns-prefetch' href='//s.w.org' /> 11<link rel="alternate" type="application/rss+xml" title="Microformats » Feed" href="https://microformats.org/feed" /> 12<link rel="alternate" type="application/rss+xml" title="Microformats » Comments Feed" href="https://microformats.org/comments/feed" /> 13<link rel="alternate" type="application/rss+xml" title="Microformats » News Category Feed" href="https://microformats.org/category/news/feed" /> 14
14<script type="text/javascript"> 15 window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/svg\/","svgExt":".svg","source":{"concatemoji":"https:\/\/microformats.org\/wordpress\/wp-includes\/js\/wp-emoji-release.min.js?ver=5.4.23"}}; 16 /*! This file is auto-generated */ 17 !function(e,a,t){var n,r,o,i=a.createElement("canvas"),p=i.getContext&&i.getContext("2d");function s(e,t){var a=String.fromCharCode;p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,e),0,0);e=i.toDataURL();return p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,t),0,0),e===i.toDataURL()}function c(e){var t=a.createElement("script");t.src=e,t.defer=t.type="text/javascript",a.getElementsByTagName("head")[0].appendChild(t)}for(o=Array("flag","emoji"),t.supports={everything:!0,everythingExceptFlag:!0},r=0;r<o.length;r++)t.supports[o[r]]=function(e){if(!p||!p.fillText)return!1;switch(p.textBaseline="top",p.font="600 32px Arial",e){case"flag":return s([127987,65039,8205,9895,65039],[127987,65039,8203,9895,65039])?!1:!s([55356,56826,55356,56819],[55356,56826,8203,55356,56819])&&!s([55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447],[55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447]);case"emoji":return!s([55357,56424,55356,57342,8205,55358,56605,8205,55357,56424,55356,57340],[55357,56424,55356,57342,8203,55358,56605,8203,55357,56424,55356,57340])}return!1}(o[r]),t.supports.everything=t.supports.everything&&t.supports[o[r]],"flag"!==o[r]&&(t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&t.supports[o[r]]);t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&!t.supports.flag,t.DOMReady=!1,t.readyCallback=function(){t.DOMReady=!0},t.supports.everything||(n=function(){t.readyCallback()},a.addEventListener?(a.addEventListener("DOMContentLoaded",n,!1),e.addEventListener("load",n,!1)):(e.attachEvent("onload",n),a.attachEvent("onreadystatechange",function(){"complete"===a.readyState&&t.readyCallback()})),(n=t.source||{}).concatemoji?c(n.concatemoji):n.wpemoji&&n.twemoji&&(c(n.twemoji),c(n.wpemoji)))}(window,document,window._wpemojiSettings); 18 </script>
18 19 <style type="text/css"> 20img.wp-smiley, 21img.emoji { 22 display: inline !important; 23 border: none !important; 24 box-shadow: none !important; 25 height: 1em !important; 26 width: 1em !important; 27 margin: 0 .07em !important; 28 vertical-align: -0.1em !important; 29 background: none !important; 30 padding: 0 !important; 31} 32</style> 33 <link rel='stylesheet' id='openid-css' href='https://microformats.org/wordpress/wp-content/plugins/openid/f/openid.css?ver=519' type='text/css' media='all' /> 34<link rel='stylesheet' id='wp-block-library-css' href='https://microformats.org/wordpress/wp-includes/css/dist/block-library/style.min.css?ver=5.4.23' type='text/css' media='all' /> 35<link rel='stylesheet' id='microformatsorg-style-css' href='https://microformats.org/wordpress/wp-content/themes/microformats/style.css?ver=1.0' type='text/css' media='screen' /> 36<link rel='stylesheet' id='microformatsorg-print-style-css' href='https://microformats.org/wordpress/wp-content/themes/microformatscss/print.css?ver=1.0' type='text/css' media='print' /> 37<link rel='https://api.w.org/' href='https://microformats.org/wp-json/' /> 38<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://microformats.org/wordpress/xmlrpc.php?rsd" /> 39<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://microformats.org/wordpress/wp-includes/wlwmanifest.xml" /> 40<meta name="generator" content="WordPress 5.4.23" /> 41<link rel="icon" href="https://microformats.org/media/2020/06/microformats-logo-150x150.png" sizes="32x32" /> 42<link rel="icon" href="https://microformats.org/media/2020/06/microformats-logo.png" sizes="192x192" /> 43<link rel="apple-touch-icon" href="https://microformats.org/media/2020/06/microformats-logo.png" /> 44<meta name="msapplication-TileImage" content="https://microformats.org/media/2020/06/microformats-logo.png" /> 45</head> 46<body class="archive category category-news category-1"> 47 48<div id="wrap"> 49 <div id="header"> 50 <h1> 51 <a href="/"> 52 <img src="https://microformats.org/wordpress/wp-content/themes/microformats/img/logo.gif" width="144" height="36" alt="microformats" /> 53 </a> 54 </h1> 55 56 <nav id="nav"> 57 <ul id="menu-main-navigation" class="primary-menu"><li id="menu-item-501" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-501"><a href="/blog">Blog</a></li> 58<li id="menu-item-511" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-511"><a href="/wiki">Wiki</a></li> 59<li id="menu-item-512" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-512"><a href="/wiki/irc">Discuss</a></li> 60<li id="menu-item-513" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-513"><a href="/wiki/about">About</a></li> 61<li id="menu-item-514" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-514"><a href="/wiki/code-tools">Code & Tools</a></li> 62<li id="menu-item-515" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-515"><a href="/wiki/get-started">Get Started</a></li> 63</ul> </nav> 64 </div> 65 66 <hr class="hide" /> 67 <div id="content"> 68 <h2 class="pagetitle">Category: News</h2> 69 <nav class="navigation pagination" role="navigation" aria-label="Posts"> 70 <h2 class="screen-reader-text">Posts navigation</h2> 71 <div class="nav-links"><span aria-current="page" class="page-numbers current">1</span> 72<a class="page-numbers" href="https://microformats.org/category/news/page/2">2</a> 73<span class="page-numbers dots">…</span> 74<a class="page-numbers" href="https://microformats.org/category/news/page/18">18</a> 75<a class="next page-numbers" href="https://microformats.org/category/news/page/2">Next Entries</a></div> 76 </nav> <article id="post-524" class="entry post-524 post type-post status-publish format-standard hentry category-news"> 77 <h3 class="entry-title" id="post-524"><a href="https://microformats.org/2022/02/19/how-to-consume-microformats-2-data" rel="bookmark" title="Permanent Link to How to Consume Microformats 2 Data">How to Consume Microformats 2 Data</a></h3> 78 <div class="entry-content"> 79 80<p>A (very) belated follow up to <a href="https://waterpigs.co.uk/articles/getting-started-with-microformats2/">Getting Started with Microformats 2</a>, covering the basics of consuming and using microformats 2 data. Originally posted <a class="u-repost-of" href="https://waterpigs.co.uk/articles/consuming-microformats/">on waterpigs.co.uk</a>.</p> 81 82<p>More and more people are using microformats 2 to mark up profiles, posts, events and other data on their personal sites, enabling developers to build applications which use this data in useful and interesting ways. Whether you want to add basic support for webmention comments to your personal site, or have ambitious plans for a structured-data-aware-social-graph-search-engine-super-feed-reader, youâre going to need a solid grasp of how to parse and handle microformats 2 data.</p> 83 84<h2 id="choose-a-parser">Choose a Parser</h2> 85 86<p>To turn a web page containing data marked up with microformats 2 (or classic microformats, if supported) into a canonical MF2 JSON data structure, youâll need a parser.</p> 87 88<p>At the time of writing, there are actively supported <a href="https://microformats.org/wiki/microformats2#Parsers">microformats 2 parsers</a> available for the following programming languages:</p> 89 90<ul> 91<li><a href="https://pkg.go.dev/willnorris.com/go/microformats">Go</a></li> 92<li><a href="https://github.com/microformats/microformats-parser">Javascript (server-side and browser)</a></li> 93<li><a href="https://github.com/indieweb/php-mf2">PHP</a></li> 94<li><a href="https://github.com/microformats/mf2py">Python</a></li> 95<li><a href="https://github.com/microformats/microformats-ruby">Ruby</a></li> 96<li><a href="https://crates.io/crates/microformats/0.2.0">Rust</a></li> 97</ul> 98 99<p>Parsers for various other languages exist, but might not be actively supported or support recent changes to the parsing specification.</p> 100 101<p>There are also various websites which you can use to experiment with microformats markup without having to download a library and write any code:</p> 102 103<ul> 104<li>My own live-updating <a href="https://waterpigs.co.uk/php-mf2/">php-mf2 sandbox</a></li> 105<li>The various parser comparison tools hosted on <a href="https://microformats.io/">microformats.io</a></li> 106<li><a class="h-card" href="https://aaronparecki.com">Aaron Parecki</a>âs <a href="http://pin13.net/mf2/">pin13.net microformats parser</a> for parsing either URLs or HTML fragments</li> 107</ul> 108 109<p>If thereâs not currently a parser available for your language of choice, you have a few options:</p> 110 111<ul> 112<li>Call the command-line tools provided by one of the existing libraries from your co
112de, and consume the JSON they provide</li> 113<li>Make use of one of the online mf2 parsers capable of parsing sites, and consume the JSON it returns (only recommended for very low volume usage!)</li> 114<li>Write your own microformats 2 parser! There are plenty of people <a href="https://indieweb.org/discuss">happy to help</a>, and a language-agnostic test suite you can plug your implementation into for testing.</li> 115</ul> 116 117<h2 id="considerations-during-fetching-and-parsing">Considerations During Fetching and Parsing</h2> 118 119<p>Most real-world microformats data is fetched from a URL, which could potentially redirect to a different URL one or more times. The final URL in the redirect chain is called the âeffective URLâ. HTML often contains relative URLs, which need to be resolved against a base URL in order to be useful out of context.</p> 120 121 122<p>If your parser has a function for âparsing microformats from a URLâ, it should deal with all of this for you. If youâre making the request yourself (e.g. to use custom caching or network settings) and then passing the response HTML and base URL to the parser, make sure to <strong>use the effective URL, not the starting URL!</strong> The parser will handle relative URL resolution, but it needs to know the correct base URL.</p> 123 124<p>When parsing microformats, an HTTP request which returns a non-200 value doesnât necessarily mean that thereâs nothing to parse! For example, a <code>410 Gone</code> response might contain a h-entry with a message explaining the deletion of whatever was there before. 125 126<h2 id="storing-raw-html-vs-parsed-mf2-json-vs-derived-data">Storing Raw HTML vs Parsed Canonical JSON vs Derived Data</h2> 127 128<p>When consuming microformats 2 data, youâll most often be fetching raw HTML from a URL, parsing it to canonical JSON, then finally processing it into a simpler, cleaned and sanitised format ready for use in your website or application. Thatâs three different representations of the same data â youâll most likely end up storing the derived data somewhere for quick access, but what about the other two?</p> 129 130<p>Experience shows that, over time:</p> 131 132<ul> 133<li>the way a particular application cleans up mf2 data will be tweaked and improved as you add new features and handle unexpected edge-cases</li> 134<li>mf2 parsers gradually get improved, fixing bugs and occasionally adding entirely new features.</li> 135</ul> 136 137<p>Therefore, if it makes sense for your use case, I recommend archiving a copy of the original HTML as well as your derived data, leaving out the intermediate canonical JSON. That way, you can easily create scripts or background jobs to update all the derived data based on the original HTML, taking advantage of both parser improvements and improvements to your own code at the same time, without having to re-fetch potentially hundreds of potentially broken links.</p> 138 139<p>As mentioned in the previous section, if you archive original HTML for re-parsing, youâll need to additionally store the effective URL for correct relative URL resolution.</p> 140 141<p>For some languages, there are already libraries (such as <a href="https://github.com/aaronpk/XRay/">XRay</a> for PHP) which will perform common cleaning and sanitisation for you. If the assumptions with which these libraries are built suit your applications, you may be able to avoid a lot of the hard work of handling raw microformats 2 data structures!</p> 142 143<p>If not, read onâ¦</p> 144 145<h2 id="microformat-structures">Navigating Microformat Structures</h2> 146 147<p>A parsed page may contain a number of microformat data structures (mf structs), in various different places.</p> 148 149<p>Take a look at <a href="http://pin13.net/mf2/?url=https%3A%2F%2Fwaterpigs.co.uk%2Farticles%2Fconsuming-microformats%2F">the parsed canonical microformats JSON for the article youâre reading right now</a>, for example.</a></p> 150 151<p><code>items</code> is a list of top-level mf structs, each of which may contain nested mf structs either under their <code>properties</code> or <code>children</code> keys.</p> 152 153<p>Each individual mf struct is guaranteed to have at least two keys, <code>type</code> and <code>properties</code>. <code>type</code> is the primary way of identifying what sort of thing that struct represents (e.g. a person, a post, an event). Structs can have more than one type if they represent multiple things at once without wanting to nest them â for example, a post detailing an event might be both a h-entry and a h-event at the same time. Structs can also have additional top-level keys such as <code>id</code> and <code>lang</code>.</p> 154 155<p>Generally speaking, <code>type</code> information is most useful when dealing with top-level mf structs, and mf structs nested under a <code>children</code> key. Nested mf structs found in <code>properties</code> will also have <code>type</code> information, but their usage is usually implied by the property name theyâre found under.</p> 156 157<p>For many common use cases (e.g. a homepage feed and profile) there are several different ways people might nest mf structs to achieve the same goals, so itâs important that your co
157de is capable of searching the entire tree, rather than just looking at the top-level mf structs. <strong>Never assume that the microformat struct youâre looking for will be in the top-level of the <code>items</code> list!</strong> You need to search the whole tree.</p> 158 159<p>I recommend writing some functions which can traverse a mf tree and return all structs which match a filtering callback. This can then be used as a basis for writing more specific convenience functions for common tasks such as finding all microformats on a page of a particular type, or where a certain property matches a certain value.</p> 160 161<p>See <a href="https://github.com/barnabywalters/php-mf-cleaner/blob/master/src/BarnabyWalters/Mf2/Functions.php">my microformats2 PHP functions</a> for some working examples.</p> 162 163<h2 id="possible-property-values">Possible Property Values</h2> 164 165<p>Each key in a mf structâs <code>properties</code> dict maps to a list of values for that property. Every property may map to multiple values, and those values may be a mixture of any of the following:</p> 166 167<p>A plain string value, containing no HTML, and leaving HTML entities unescaped (e.g. <code><</code>)</p> 168 169<pre><code>{ 170 <span class="hljs-attr">"items"</span>: [{ 171 <span class="hljs-attr">"type"</span>: [<span class="hljs-string">"h-card"</span>], 172 <span class="hljs-attr">"properties"</span>: { 173 <span class="hljs-attr">"name"</span>: [<span class="hljs-string">"Barnaby Walters"</span>] 174 } 175 }] 176} 177</code></pre> 178 179<p>(In future examples I will leave out the encapsulating <code>{"items": [{"type": [â¢â¢â¢], â¢â¢â¢}]}</code> for brevity, focusing on the <code>properties</code> key of a single mf struct.)</p> 180 181<p>An embedded HTML struct, containing two keys: <code>html</code>, which maps to an HTML representation of the property, and <code>value</code>, mapping to a plain text version.</p> 182 183<pre><code>"properties": { 184 "content": [{ 185 "html": "<p>The content <span class="hljs-keyword">of</span> a post, <span class="hljs-keyword">as</span> <strong>raw HTML</strong> (<span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span>).</p>", 186 "value": "The content <span class="hljs-keyword">of</span> a post, <span class="hljs-keyword">as</span> raw HTML (<span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span>)." 187 }] 188} 189</code></pre> 190<p>An img/alt struct, containing the URL of a parsed image under <code>value</code>, and its alt text under <code>alt</code>.</p> 191 192<pre><code><span class="hljs-string">"properties"</span>: { 193 <span class="hljs-string">"photo"</span>: [{ 194 <span class="hljs-string">"value"</span>: <span class="hljs-string">"https://example.com/profile-photo.jpg"</span>, 195 <span class="hljs-string">"alt"</span>: <span class="hljs-string">"Example Person"</span> 196 }] 197} 198</code></pre> 199<p>A nested microformat data structure, with an additional <code>value</code> key containing a plaintext representation of the data contained within.</p> 200 201<pre><code><span class="hljs-string">"properties"</span>: { 202 <span class="hljs-string">"author"</span>: [{ 203 <span class="hljs-string">"type"</span>: [<span class="hljs-string">"h-card"</span>], 204 <span class="hljs-string">"properties"</span>: { 205 <span class="hljs-string">"name"</span>: [<span class="hljs-string">"Barnaby Walters"</span>] 206 }, 207 <span class="hljs-string">"value"</span>: <span class="hljs-string">"Barnaby Walters</span> 208 }] 209} 210</code></pre> 211<p>All properties may have more than one value. In cases where you expect a single property value (e.g. <code>name</code>), simply take the first one you find, and in cases where you expect multiple values, use all values you consider valid. There are also some cases where it may make sense to use multiple values, but to prioritise one based on some heuristic â for example, an h-card may have multiple <code>url</code> values, in which case the first one is usually the âcanonicalâ URL, and further URLs refer to external profiles.</p> 212 213<p>Letâs look at the implications of each of the potential property value structures in turn.</p> 214 215<p>Firstly, <strong>Never assume that a property value will be a plaintext string</strong>. Microformats publishers can nest microformats, embedded content and img/alt structures in a variety of different ways, and your consuming code should be as flexible as possible.</p> 216 217<p>To partially make up for this complexity, you can <strong>always rely on the <code>value</code> key of nested structs to provide you with an equivalent plaintext value</strong>, regardless of what type of struct youâve found.</p> 218 219<p>When you start consuming microformats 2, write a function like this, and get into the habit of using it <strong>every time</strong> you want a single, plaintext value from a property:</p> 220 221<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_first_plaintext</span><span class="hljs-params">(mf_struct, property_name)</span>:</span> 222 <span class="hljs-keyword">try</span>: 223 first_val = mf_struct[<span class="hljs-string">'properties'</span>][property_name][<span class="hljs-number">0</span>] 224 <span class="hljs-keyword">if</span> isinstance(first_val, str):
225 <span class="hljs-keyword">return</span> first_val 226 <span class="hljs-keyword">else</span>: 227 <span class="hljs-keyword">return</span> first_val[<span class="hljs-string">'value'</span>] 228 <span class="hljs-keyword">except</span> (IndexError, KeyError): 229 <span class="hljs-keyword">return</span> <span class="hljs-keyword">None</span> 230</code></pre> 231 232<p>Secondly, <strong>Never assume that a particular property will contain an embedded HTML struct</strong> â this usually applies to <code>content</code>, but is relevant anywhere your application expects embedded HTML. If you want to reliably get a value encoded as raw HTML, then you need to:</p> 233 234<ol> 235<li>Check whether the first property value is an embedded HTML struct (i.e. has an <code>html</code> key). If so, take the value of the <code>html</code> key</li> 236<li>Otherwise, get the first plaintext property value using the approach above, and HTML-escape it</li> 237<li>If neither is found, the property has no value.</li> 238</ol> 239 240<p>In Python 3.5+, that could look something like this:</p> 241 242<pre><code><span class="hljs-keyword">from</span> html <span class="hljs-keyword">import</span> escape 243 244<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_first_html</span><span class="hljs-params">(mf_struct, property_name)</span>:</span> 245 <span class="hljs-keyword">try</span>: 246 first_val = mf_struct[<span class="hljs-string">'properties'</span>][property_name][<span class="hljs-number">0</span>] 247 <span class="hljs-keyword">if</span> isinstance(first_val, dict) <span class="hljs-keyword">and</span> <span class="hljs-string">'html'</span> <span class="hljs-keyword">in</span> first_val: 248 <span class="hljs-keyword">return</span> first_val[<span class="hljs-string">'html'</span>] 249 <span class="hljs-keyword">else</span>: 250 plaintext_val = get_first_plaintext(mf_struct, property_name) 251 252 <span class="hljs-keyword">if</span> plaintext_val <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">None</span>: 253 plaintext_val = escape(plaintext_val) 254 255 <span class="hljs-keyword">return</span> plaintext_val 256 <span class="hljs-keyword">except</span> (IndexError, KeyError): 257 <span class="hljs-keyword">return</span> <span class="hljs-keyword">None</span> 258</code></pre> 259<p>In some cases, it may make sense for your application to be aware of whether a value was parsed as embedded HTML or a plain text string, and to store/treat them differently. In all other cases, <strong>always</strong> use a function like this when youâre expecting embedded HTML data.</p> 260 261<p>Thirdly, when expecting an image URL, check for an img/alt structure, falling back to the plain text value (and either assuming an empty alt text or inferring an appropriate one, depending on your specific use case). Something like this could be a good starting point:</p> 262 263<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_img_alt</span><span class="hljs-params">(mf_struct, property_name)</span>:</span> 264 <span class="hljs-keyword">try</span>: 265 first_val = mf_struct[<span class="hljs-string">'properties'</span>][property_name][<span class="hljs-number">0</span>] 266 <span class="hljs-keyword">if</span> isinstance(first_val, dict) <span class="hljs-keyword">and</span> <span class="hljs-string">'alt'</span> <span class="hljs-keyword">in</span> first_val: 267 <span class="hljs-keyword">return</span> first_val 268 <span class="hljs-keyword">else</span>: 269 plaintext_val = get_first_plaintext(mf_struct, property_name) 270 271 <span class="hljs-keyword">if</span> plaintext_val <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">None</span>: 272 <span class="hljs-keyword">return</span> {<span class="hljs-string">'value'</span>: plaintext_val, <span class="hljs-string">'alt'</span>: <span class="hljs-string">''</span>} 273 274 <span class="hljs-keyword">return</span> <span class="hljs-keyword">None</span> 275 <span class="hljs-keyword">except</span> (IndexError, KeyError): 276 <span class="hljs-keyword">return</span> <span class="hljs-keyword">None</span> 277</code></pre> 278<p>Finally, in cases where you expect a nested microformat, you might end up getting something else. This is the hardest case to deal with, and the one which depen
278ds the most on the specific data and use-case youâre dealing with. For example, if youâre expecting a nested h-card under an <code>author</code> property, but get something else, you could use any of the following approaches:</p> 279 280<ul> 281<li>If you got a plain string which doesnât look like a URL, treat it as the <code>name</code> property of an implied h-card structure with no other properties (and if you need a URL, you could potentially take the hostname of the effective URL, if it works in context as a useful fallback value)</li> 282<li>If you got an img alt struct, you could treat the <code>value</code> as the <code>photo</code> property, the <code>alt</code> as the <code>name</code> property, and potentially even take the hostname of the <code>photo</code> URL to be the implied fallback <code>url</code> property (although thatâs pushing it a bit, and in most cases itâs probably better to just leave out the <code>url</code>)</li> 283<li>If you got an embedded HTML struct, take its plaintext <code>value</code> and use one of the first two approaches</li> 284<li>If you got a plain string, check to see if it looks like a URL. If so, fetch that URL and look for a representative h-card to use as the author value</li> 285<li>If you get an embedded mf struct with a <code>url</code> property but no <code>photo</code>, you could fetch the <code>url</code>, look for a representative h-card (more on that in the next section) and see if it has a <code>photo</code> property</li> 286<li>Treat the <code>author</code> property as invalid and run the h-entry (or entire page if relevant) through the <a href="https://indieweb.org/authorship-spec">authorship algorithm</a></li> 287</ul> 288 289<p>The first three are general principles which can be applied to many scenarios where you expect an embedded mf struct but find something else. The last three, however, are examples of a common trend in consuming microformats 2 data: for many common use-cases, there are well-thought-through algorithms you can use to interpret data in a standardised way.</p> 290 291<h2 id="know-your-algorithms-and-vocabularies">Know Your Algorithms and Vocabularies</h2> 292 293<p>The authorship algorithm mentioned above is one of several more-or-less formally established algorithms used to solve common problems in indieweb usages of microformats 2. Some others which are worth knowing about include:</p> 294 295<ul> 296<li>âWho wrote this post?â: <a href="https://indieweb.org/authorship-spec">authorship algorithm</a></li> 297<li>âThereâs more than one h-card on this page, which one should I use?â: <a href="https://microformats.org/wiki/representative-h-card-parsing">representative h-card</a></li> 298<li>âI want to get a paginated feed of posts from this pageâ: <a href="https://indieweb.org/feed#How_To_Consume">How to consume h-feed</a></li> 299<li>âHow do I find and display the main post on this page?â: <a href="https://indieweb.org/authorship-spec">How to consume h-entry</a></li> 300<li>âI received a response to one of my posts via webmention, how do I display it?â: <a href="https://indieweb.org/comments#How_to_display">How to display comments</a></li> 301</ul> 302 303<p>Library implementations of these algorithms exist for some languages, although they often deviate slightly from the exact text. See if you can find one which meets your needs, and if not, write your own and share it with the community!</p> 304 305<p>In addition to the formal consumption algorithms, itâs worth looking through the definitions of the microformats vocabularies youâre using (as well as testing with real-world data) and adding support for properties or publishing techniques you might not have thought of the first time around. Some examples to get you started:</p> 306 307<ul> 308<li>If an h-card has no valid <code>photo</code>, see if thereâs a valid <code>logo</code> you can use instead</li> 309<li>When presenting a h-entry with a featured photo, check both the <code>photo</code> property and the <code>featured</code> property, as one or the other might be used in different scenarios</li> 310<li>When dealing with address or location data (e.g. on an h-card, h-entry or h-event), be aware that either might be present in various different forms. Co-ordinates might be separate <code>latitude</code> and <code>longitude</code> properties, a combined plaintext <code>geo</code> property, or an embedded <code>h-geo</code>. Addresses might be separate top-level properties or an embedded h-adr. There are many variations which are totally valid to publish, and your consuming code should be as liberal as possible in what it accepts.</li> 311<li>If a h-entry contains images which are marked up with <code>u-photo</code> within the <code>e-content</code>, theyâll be present both in the <code>content</code> <code>html</code> key and also under the <code>photo</code> property. If your app shows the embedded <code>content</code> HTML rather than using the plaintext version, and also supports <code>photo</code> properties (which may also be present outside the <code>content</code>
311), you may have to sniff the presence of photos within the <code>content</code>, and either remove them from it or ignore the corresponding <code>photo</code> properties to avoid showing photos twice.</li> 312</ul> 313 314<h2 id="sanitise-validate-and-truncate">Sanitise, Validate, and Truncate</h2> 315 316<p>In the vast majority of cases, consuming microformats 2 data involves handling, storing and potentially re-publishing untrusted and potentially dangerous input data. Preventing XSS and other attacks is out of the scope of the microformats parsing algorithm, so the data your parser gives you is just as dangerous as the original source. You need to take your own measures for sanitising and truncating it so you can store and display it safely.</p> 317 318<p>Covering every possible injection and XSS attack is out of the scope of this article, so I highly recommend referring to the OWASP resources on <a href="https://owasp.org/www-community/attacks/xss/">XSS Prevention</a>, <a href="https://owasp.org/www-community/attacks/Unicode_Encoding">Unicode Attacks</a> and <a href="https://owasp.org/Top10/A03_2021-Injection/">Injection Attacks</a> for more information.</p> 319 320<p>Other than that, the following ideas are a good start:</p> 321 322<ul> 323<li>Use plaintext values where possible, only using embedded HTML when absolutely necessary</li> 324<li>Pass everything (HTML or not) through a well-respected HTML sanitizer such as PHPâs <a href="https://github.com/ezyang/htmlpurifier">HTML Purifier</a>. Configure it to make sure that embedded HTML canât interfere with your own markup or CSS. It probably shouldnât contain any javascript ever, either.</li> 325<li>In any case where youâre expecting a value with a specific format, validate it as appropriate.</li> 326<li>More specifically, everywhere that you expect a URL, check that what you got was actually a URL. If youâre using the URL as an image, consider fetching it an checking its content type</li> 327<li>Consider either proxying resource such as images, or storing local copies of them (reducing size and resolution as necessary), to avoid mixed content issues, potential attacks, and missing images if the links break in the future.</li> 328<li>Decide on relevant maximum length values for each separate piece of external content, and truncate them as necessary. Ideally, use a language-aware truncation algorithm to avoid breaking words apart. When the content of a post is truncated, consider adding a âRead Moreâ link for convenience.</li> 329</ul> 330 331<h2 id="test-with-real-world-data">Test with Real-World Data</h2> 332 333<p>The web is a diverse place, and microformats are a flexible, permissive method of marking up structured data. There are often several different yet perfectly valid ways to achieve the same goal, and as a good consumer of mf2 data, your application should strive to accept as many of them as possible!</p> 334 335<p>The best way to test this is with <em>real world data</em>. If your application is built with a particular source of data in mind, then start off with testing it against that. If you want to be able to handle a wider variety of sources, the best way is to determine what vocabularies and publishing use-cases your application consumes, and look at the Examples sections of the relevant <a href="https://indieweb.org">
335indieweb.org</a> wiki pages for real-world sites to test your code against.</p> 336 337<p>Donât forget to test your code against examples youâve published on your own personal site!</p> 338 339<h2 id="next-steps">Next Steps</h2> 340 341<p>Hopefully this article helped you avoid a lot of common gotchas, and gave you a good head-start towards successfully consuming real-world microformats 2 data.</p> 342 343<p>If you have questions or issues, or want to share something cool youâve built, come and join us in the <a href="https://indieweb.org/discuss">indieweb chat room</a>.</p> 344 </div> 345 346 <ul class="post-info"> 347 <li><a class="updated" href="https://microformats.org/2022/02/19/how-to-consume-microformats-2-data" rel="bookmark" title="Permanent Link to How to Consume Microformats 2 Data"> 348 <span class="value-title" title="2022-02-19T11:48:15"> </span> 349 February 19th, 2022 </a> 350 </li> 351 <li> 352 <address class="author vcard"> 353 <a class="url fn" href=""> 354 <img alt='' src='https://secure.gravatar.com/avatar/4a57cddee3c50aefa893005dcdd33b64?s=16&d=mm&r=pg' srcset='https://secure.gravatar.com/avatar/4a57cddee3c50aefa893005dcdd33b64?s=32&d=mm&r=pg 2x' class='avatar avatar-16 photo' height='16' width='16' /> waterpigs.co.uk/ </a> 355 </address> 356 </li> 357 <li><span>Comments Off<span class="screen-reader-text"> on How to Consume Microformats 2 Data</span></span></li> 358 <li> 359 </li> 360 </ul> 361</article> 362 363 <article id="post-491" class="entry post-491 post type-post status-publish format-standard hentry category-news tag-indieweb tag-microformats2"> 364 <h3 class="entry-title" id="post-491"><a href="https://microformats.org/2020/03/04/google-confirms-microformats-are-still-a-recommended-metadata-format-for-content" rel="bookmark" title="Permanent Link to Google confirms Microformats are still a recommended metadata format for content">Google confirms Microformats are still a recommended metadata format for content</a></h3> 365 <div class="entry-content"> 366 <div> 367<p>This post <a href="https://www.jvt.me/posts/2020/03/02/google-microformats-support/" rel="canonical">originally appeared on Jamie Tanna’s site</a>.</p> 368<p>Google announced that they are <a href="https://webmasters.googleblog.com/2020/01/data-vocabulary.html">removing support for the data-vocabulary metadata</a> markup that could be used to provide rich search results on its Search Engine.</p> 369<p>In a Twitter exchange, John Mueller, a Webmaster Trends Analyst at Google, confirmed that <a href="https://microformats.io">Microformats</a> are still being supported by Google at this time:</p> 370<blockquote> 371<p lang="en" dir="ltr">Yes, we still support them.</p> 372<p>â ð John ð (@JohnMu) <a href="https://twitter.com/JohnMu/status/1219739919268155392?ref_src=twsrc%5Etfw">January 21, 2020</a></p></blockquote> 373<p>John also confirmed that he knows of no upcoming plans to deprecate Microformats:</p> 374<blockquote> 375<p lang="en" dir="ltr">We don’t have any plans for changes to announce there at the moment. I don’t know off-hand how broadly microformats are used, my guess is it’s much more than data-vocabulary. That said … <a href="https://t.co/ZCE7rTKmPa">https://t.co/ZCE7rTKmPa</a></p> 376<p>â ð John ð (@JohnMu) <a href="https://twitter.com/JohnMu/status/1219597542318538752?ref_src=twsrc%5Etfw">January 21, 2020</a></p></blockquote> 377<p>This is an especially great result due to the way that Google is quite happy to abandon various metadata formats, as noted in our <a href="http://microformats.org/2012/06/25/microformats-org-at-7#challenges">7th anniversary blog post</a>, almost 8 years ago. With this announcement, Microformats are now the longest-supported metadata format that Google parses, <a href="http://microformats.org/wiki/google-search">since at least 2009</a>!</p> 378<p>With the continued growth of Microformats across the <a href="https://indieweb.org">IndieWeb</a>, we expect that Google will extend its Microformats support accordingly.</p> 379</div> 380 </div> 381 <div class="post-tags"> 382 <h4>Tags for this entry</h4> 383 <ul><li><a href="https://microformats.org/tag/indieweb" rel="tag">indieweb</a>, </li><li><a href="https://microformats.org/tag/microformats2" rel="tag">microformats2</a></li></ul> </div> 384 385 <ul class="post-info"> 386 <li><a class="updated" href="https://microformats.org/2020/03/04/google-confirms-microformats-are-still-a-recommended-metadata-format-for-content" rel="bookmark" title="Permanent Link to Google confirms Microformats are still a recommended metadata format for content"> 387 <span class="value-title" title="2020-03-04T10:48:02"> </span> 388 March 4th, 2020 </a> 389 </li> 390 <li> 391 <address class="author vcard"> 392 <a class="url fn" href=""> 393 <img alt='' src='https://secure.gravatar.com/avatar/702c2c3657b87396c41f14251af663c4?s=16&d=mm&r=pg' srcset='https://secure.gravatar.com/avatar/702c2c3657b87396c41f14251af663c4?s=32&d=mm&r=pg 2x' class='avatar avatar-16 photo' height='16' width='16' /> jamietanna </a> 394 </address> 395 </li> 396 <li><a href="https://microformats.org/2020/03/04/google-confirms-microformats-are-still-a-recommended-metadata-format-for-content#comments">1 Comment</a></li> 397 <li> 398 </li> 399 </ul> 400</article> 401 402 <article id="post-480" class="entry post-480 post type-post status-publish format-standard hentry category-news tag-microformats2"> 403 <h3 class="entry-title" id="post-480"><a href="https://microformats.org/2018/06/22/microformats-org-year-14-welcome-new-admins" rel="bookmark" title="Permanent Link to microformats.org Year 14 â Welcome New Admins">microformats.org Year 14 â Welcome New Admins</a></h3> 404 <div class="entry-content"> 405 <p>In microformats.org year 14, we welcome <a href="http://microformats.org/wiki/admins">new admins</a>: <a href="https://aaronparecki.com/">Aaron Parecki</a>, <a href="https://gregorlove.com/">Gregor Morrill</a>, <a href="https://vanderven.se/martijn/">Martijn van der Ven</a>, and <a href="https://www.svenknebel.de/">Sven Knebel</a>! All have been active for years, helping welcome new members and doing essential wiki gardening & <a href="http://microformats.org/wiki/microformats2#Implementations">microformats2 parser updates</a>!</p> 406<p>Originally posted at: <a href="http://tantek.com/2018/173/t2/microformats-welcome-new-admins">tantek.com</a></p> 407 </div> 408 <div class="post-tags"> 409 <h4>Tags for this entry</h4> 410 <ul><li><a href="https://microformats.org/tag/microformats2" rel="tag">microformats2</a></li></ul> </div> 411 412 <ul class="post-info"> 413 <li><a class="updated" href="https://microformats.org/2018/06/22/microformats-org-year-14-welcome-new-admins" rel="bookmark" title="Permanent Link to microformats.org Year 14 â Welcome New Admins"> 414 <span class="value-title" title="2018-06-22T15:14:41"> </span> 415 June 22nd, 2018 </a> 416 </li> 417 <li> 418 <address class="author vcard"> 419 <a class="url fn" href=""> 420 <img alt='' src='https://secure.gravatar.com/avatar/a46720c1ca0c1a871682adbbdfe48567?s=16&d=mm&r=pg' srcset='https://secure.gravatar.com/avatar/a46720c1ca0c1a871682adbbdfe48567?s=32&d=mm&r=pg 2x' class='avatar avatar-16 photo' height='16' width='16' /> Tantek </a> 421 </address> 422 </li> 423 <li><span>Comments Off<span class="screen-reader-text"> on microformats.org Year 14 â Welcome New Admins</span></span></li> 424 <li> 425 </li> 426 </ul> 427</article> 428 429 <article id="post-475" class="entry post-475 post type-post status-publish format-standard hentry category-news tag-indieweb tag-microformats2"> 430 <h3 class="entry-title" id="post-475"><a href="https://microformats.org/2018/06/21/happy-13th-to-microformats-org" rel="bookmark" title="Permanent Link to Happy 13th to microformats.org!">Happy 13th to microformats.org!</a></h3> 431 <div class="entry-content"> 432 <p>With more use of <a href="http://microformats.org/wiki/microformats2">microformats2</a>, especially among the growing <a href="https://indieweb.org/">indieweb</a> network of websites, weâve iterated <a href="http://microformats.org/wiki/microformats2-parsing">key</a> <a href="http://microformats.org/wiki/h-feed">specs</a> for real-world needs and are seeing more active community members. More updates & posts coming up!</p> 433<p>Originally posted on <a href="http://tantek.com/2018/171/t2/happy-13th-microformats-org">tantek.com</a>.</p> 434 </div> 435 <div class="post-tags"> 436 <h4>Tags for this entry</h4> 437 <ul><li><a href="https://microformats.org/tag/indieweb" rel="tag">indieweb</a>, </li><li><a href="https://microformats.org/tag/microformats2" rel="tag">microformats2</a></li></ul> </div> 438 439 <ul class="post-info"> 440 <li><a class="updated" href="https://microformats.org/2018/06/21/happy-13th-to-microformats-org" rel="bookmark" title="Permanent Link to Happy 13th to microformats.org!">
441 <span class="value-title" title="2018-06-21T08:40:46"> </span> 442 June 21st, 2018 </a> 443 </li> 444 <li> 445 <address class="author vcard"> 446 <a class="url fn" href=""> 447 <img alt='' src='https://secure.gravatar.com/avatar/a46720c1ca0c1a871682adbbdfe48567?s=16&d=mm&r=pg' srcset='https://secure.gravatar.com/avatar/a46720c1ca0c1a871682adbbdfe48567?s=32&d=mm&r=pg 2x' class='avatar avatar-16 photo' height='16' width='16' /> Tantek </a> 448 </address> 449 </li> 450 <li><span>Comments Off<span class="screen-reader-text"> on Happy 13th to microformats.org!</span></span></li> 451 <li> 452 </li> 453 </ul> 454</article> 455 456 <article id="post-469" class="entry post-469 post type-post status-publish format-standard hentry category-news"> 457 <h3 class="entry-title" id="post-469"><a href="https://microformats.org/2017/06/22/improving-the-php-mf2-parser" rel="bookmark" title="Permanent Link to Improving the php-mf2 parser">Improving the php-mf2 parser</a></h3> 458 <div class="entry-content"> 459 <p>During the past year, the popular <a href="https://github.com/indieweb/php-mf2">php-mf2</a> microformats parser has received quite a few improvements. My site runs ProcessWire and one of the plugins for it uses php-mf2, so I have been spending some time on it.</p> 460<p>My own experience with microformats started when I discovered the <a href="http://microformats.org/wiki/hcard">hCard microformat</a>. I was impressed with the novelty of adding some simple HTML classes around contact information and having a browser extension parse it into an address book. Years later, when I started to get involved in the IndieWeb community, I learned a lot more about microformats2 and they became a key building block of my personal site.</p> 461<p>php-mf2 is now much better at backwards-compatible parsing of microformats1. This is important because software should be able to consistently consume content whether itâs marked up with microformats1, microformats2, or a combination. An experimental feature for parsing language attributes has also been added. Finally, itâs now using the microformats test suite. Several other parsers use this test suite as well. This will make it easier to catch bugs and improve all of the different parsers.</p> 462<p>php-mf2 is a stable library thatâs ready to be installed in your software to start consuming microformats. It is currently used in <a href="https://withknown.com">Known</a>, <a href="https://wordpress.org/plugins/semantic-linkbacks/">WordPress plugins</a>, and <a href="https://modules.processwire.com/modules/webmention/">ProcessWire plugins</a> for richer social interactions. Itâs also used in tools like <a href="https://github.com/aaronpk/XRay">XRay</a> and <a href="https://microformats.io">microformats.io</a>. Iâm looking forward to more improvements to php-mf2 in the coming year as well as more software using it!</p> 463<p>Original published at: <a href="https://gregorlove.com/2017/06/improving-the-php-mf2-parser/" rel="canonical">https://gregorlove.com/2017/06/improving-the-php-mf2-parser/</a></p> 464 </div> 465 466 <ul class="post-info"> 467 <li><a class="updated" href="https://microformats.org/2017/06/22/improving-the-php-mf2-parser" rel="bookmark" title="Permanent Link to Improving the php-mf2 parser"> 468 <span class="value-title" title="2017-06-22T09:13:53"> </span> 469 June 22nd, 2017 </a> 470 </li> 471 <li> 472 <address class="author vcard"> 473 <a class="url fn" href=""> 474 <img alt='' src='https://secure.gravatar.com/avatar/aca81ab5bf69a4626c91edc811cea208?s=16&d=mm&r=pg' srcset='https://secure.gravatar.com/avatar/aca81ab5bf69a4626c91edc811cea208?s=32&d=mm&r=pg 2x' class='avatar avatar-16 photo' height='16' width='16' /> gRegor Morrill </a> 475 </address> 476 </li> 477 <li><span>Comments Off<span class="screen-reader-text"> on Improving the php-mf2 parser</span></span></li> 478 <li> 479 </li> 480 </ul> 481</article> 482 483 484 485 <nav class="navigation pagination" role="navigation" aria-label="Posts"> 486 <h2 class="screen-reader-text">Posts navigation</h2> 487 <div class="nav-links"><span aria-current="page" class="page-numbers current">1</span> 488<a class="page-numbers" href="https://microformats.org/category/news/page/2">2</a> 489<span class="page-numbers dots">…</span> 490<a class="page-numbers" href="https://microformats.org/category/news/page/18">18</a> 491<a class="next page-numbers" href="https://microformats.org/category/news/page/2">Next Entries</a></div> 492 </nav> 493 </div> 494 495<hr class="hide" /> 496 497<div id="sidebar"> 498<div id="categories-137139891" class="box widget widget_categories"><div class="box-inner"><h3>Post Categories</h3> <ul> 499 <li class="cat-item cat-item-22"><a href="https://microformats.org/category/events" title="Events about or including microformats; parties, conferences and hack days.">Events</a> 500</li> 501 <li class="cat-item cat-item-1 current-cat"><a aria-current="page" href="https://microformats.org/category/news">News</a> 502</li> 503 <li class="cat-item cat-item-39"><a href="https://microformats.org/category/this-week" title="This Week in Microformats is a semi-regular update of what's happened on the microformats.org wiki and mailing lists.">This Week in Microformats</a> 504</li> 505 </ul> 506 </div></div> 507 <div class="box"> 508 <div class="box-inner"> 509 <form method="get" id="search" action="/wordpress/index.php"> 510 <div> 511 <input type="text" value="search blog" name="s" id="search-text" onfocus="if(this.value=='' || this.value=='search blog'){this.value='';}" onblur="if(this.value==''){this.value='search blog';}" /> 512 <input type="image" id="search-submit" alt="Search" src="https://microformats.org/wordpress/wp-content/themes/microformats/img/btn-search.gif" /> 513 </div> 514</form> 515 </div> 516 </div> 517 518 <div class="box"> 519 <div class="box-inner"> 520 </div> 521 </div> 522 523</div> <!-- end #sidebar --> 524 525<hr class="hide" /> 526 527<div id="footer"> 528 <p>Powered by <a href="http://wordpress.org">WordPress</a> | Hosting sponsored by <a href="https://www.linode.com/?r=f27e4bad029e8c2a2bf8737bf12439133dd4b977">Linode</a> | <a href="http://no-www.org/">No WWW</a>. 529 </p> 530</div> 531 532</div> <!-- end #wrap --> 533
533<script src="http://www.google-analytics.com/urchin.js" type="text/javascript" /> 534
534<script type="text/javascript"> 535 _uacct = "UA-1889385-1"; 536 urchinTracker(); 537 </script>
537 538
538<script type='text/javascript' src='https://microformats.org/wordpress/wp-includes/js/wp-embed.min.js?ver=5.4.23'></script>
538 539</body> 540</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.