Meta Tags to Microformats

Earlier today, Jamie Tanna announced the opengraph-mf2 library and hosted project. It takes OpenGraph meta tags and converts them to microformats.

I do the same thing as one of the many pieces of my somewhat messy Parse This library. Parse This, which is designed to feed WordPress plugins, forms the basis of the reply-contexts in the Post Kinds plugin, the parsing for the Yarns Microsub plugin, and my newly released bookmarks plugin. In all cases, it tries to extract as much data about the URL sent to it, and return it in microformats 2 json, or the simplified jf2 format.

Jamie’s code is a simple 80 lines that takes a few tags and tries to convert them. I ran through every meta tag I could find by looking at dozens of different sites, so I was inspired to document same.

First of all, if you look at MDN’s definition for the meta tag, it states that if the name property is set, the meta element applies to the entire page, but if the itemprop property is set, that’s user-defined metadata. The content property contains the value for the name attribute. There is no mention of the attribute property in the HTML spec, but it is mentioned in the OpenGraph protocol.

I take name, property, or itemprop and map it to the key in an associative array, then content is the value. For values with curies(:), I use that to create a nested array, which is what I use to map properties.

There are common classic meta names that are longstanding and defined in the HTML specification, such as author, description, and keywords. If nothing else, this might generate some simple information.

Moving up a level to OpenGraph…there are several common metadata fields, namespaced with og.

  • og:title – this would map to p-name
  • Media – Some media has the :secure_url addition for the https version of the image. This is still used, although the modern utility is sometimes questionable.
    • og:image – this would map to u-photo.
    • og:video – this would map to u-video
    • og:audio – this would map to u-audio
  • og:url – this would map to u-url
  • og:description – this would map to p-summary
  • og:longitude, og:latitude can map to the equivalent location
  • og:type – The type is a bit harder to map, but can be used as hinting otherwise. Article as a type would be considered h-entry, profile would be h-card, music and video types would be h-cite.

Of the various types, music and video types are not really represented well in Microformats. So let’s focus on article first.

  • article:published_time – mapped to the dt-published property
  • article:modified_time – mapped to the dt-updated property
  • article:author – mapped to the author property

Many of the types have a tag property, that can have one or more tags…which get mapped to category.

Jamie opted to map the Twitter namespace properties as a secondary factor. I opted not to. The namespace is from their Cards specification, which is really just another OGP namespace. The problem is that they don’t provide an author name or website, only their Twitter handle. The majority of sites I viewed had both the og and the twitter namespaces, and I never got anything from the twitter namespace that wasn’t in the og namespace except Twitter specific details, which I wasn’t interested in. Facebook was responsible for OGP, so most people want to cover both sites, so they have both.

I did opt to look for the custom namespace for FourSquare venues, which is playfourquare, for latitude longitude. I also considered the presence of the namespace to indicate a FourSquare venue, and therefore an h-card.

  • playfoursquare:location:latitude – maps to p-latitude
  • playfoursquare:location:longitude – maps to p-longitude

After the OGP tags, I also looked for some other common meta tag names.

Some academic sources use Dublin Core properties in meta tags:

    • DC.Creator – p-author
    • DC.Title – p-name
    • DC.Date – dt-published
    • DC.Date.modified – dt-updated

Parse.ly, which is part of WordPress VIP, has its own markup.

  • parsely-title – p-name
  • parsely-link – u-url
  • parsely-image-url – u-photo
  • parse-type – post is h-entry, index would be h-feed
  • parsely-pub-date – Publication date
  • parsely-author as p-author
  • parsely-tags as the p-category
  • They also offer the property parsely-metadata for other fields which is json encoded.

I also convert JSON-LD to microformats, but that’s another story

 

Episode 15 – micro.blog()


After a gap of over a year, we resumed our IndieWeb podcast and got together to discuss what has been going on, how we have been building the community during the pandemic, and about our topic of micro.blog. There is also a video attached this time.

 

IndieAuth 3.5.0 for WordPress Released

Earlier in the week, I noted the release of IndieAuth 3.5.0, but I didn’t explain the major under the hood changes that occurred here in a post, which I need to do as at least one person is experiencing issues(probably necessitating a 3.5.1 as soon as I figure out why.)

I also noted I forgot to describe this clearly in the readme, if people read changelogs and will have to correct that as well.  I wrote some of this code in January and it was merged, but didn’t release it till July, so…

IndieAuth 3.5.0 implements scope support. Previously, scopes were handled by the Micropub plugin. It would check what scopes you had and implement accordingly. But WordPress does not have the concept of scopes. It has the concept of capabilities. And users have roles, which are collections of capabilities.

So Indieauth 3.5.0 implements scopes also as collections of capabilities. If you are doing a capability check in WordPress, and the request was authenticated with an IndieAuth token specifically, it will filter your capabilities by the ones defined in the scopes the token has.

That means if your user didn’t have the capability to begin with, you can’t use it. In a future version, I’ve considered on the authorization screen, not even issuing a token with that scope, but this is ultimately more secure than before.

It means that plugins don’t have to understand scope at all. They just have to enforce and support native WordPress capabilities, which they should anyway.

For now, the system only supports built-in capabilities, but there is nothing that says it cannot move to custom capabilities as needed, as everything is filterable and we accept pull requests.

The second big change I did mention in the changelog brings the code to support using a remote IndieAuth endpoint back into this code. However, it is disabled by default. This is based on the code removed from Micropub, which had a parallel IndieAuth class that was only used when the IndieAuth plugin was not enabled. By having it here, it allows anyone who wants to use it to enable it, but simplifies the experience for the bulk of users. It also allows it to be enhanced by any of the scope or other enhancements put into the main plugin.

The plugin also simplifies the site checks to ensure that your site will work with the plugin, putting them into the Site Health checks where they logically belong. This includes an SSL check and a Authorization header check.

People Seem Confused about IndieAuth

When I first started in the IndieWeb community, IndieAuth confused me. It confused me up until I built an IndieAuth endpoint for WordPress. It may confuse you as well. And that has been a problem in its adoption.

The biggest confusion seems to be conflating IndieAuth and IndieAuth.com. IndieAuth.com is a reference implementation of the protocol built by Aaron Parecki, who edited the IndieAuth specification. Aaron works extensively with OAuth as part of his day job.

OAuth is that technology you see all around the web. It allows you to log into one site using the credentials of another. So, “Sign in with Google”, “Sign in with Facebook”, etc. The site you signed into uses one of these sites to verify your identity.

IndieAuth is a layer on top of that. It allows you to sign in with your website. So, to login you provide the URL of that site, which represents your identity. The client goes and retrieves your site, and looks for hidden links to your IndieAuth endpoints and asks you to verify your identity to it. Then, once you have, it issues permission to the client to act as you, with whatever permissions you have approved.

IndieAuth.com, being a reference implementation, wouldn’t know how to verify your identity. So, it uses a workaround called RelMeAuth. If you put a link on your website to your GitHub account, or other sites that support OAuth, marked up in HTML with rel=”me”, it would go to the sites of those services it supported, check to see if, using the GitHub example, if your GitHub profile had a link back to your website. This would prove your GitHub account and your website were owned by the same person. Then if you could successfully authenticate to GitHub, it would then issue the client the permissions it requested.

Since for IndieAuth.com to work with your site, you had to link to it on your site in a certain way, designating it as the authentication endpoint for that URL, it meant that unless someone had the ability to edit your page(a bigger problem), they couldn’t use it to get into your site.

But IndieAuth.com isn’t meant to be a permanent service and the fact people think that IndieAuth.com = IndieAuth the protocol is a problem. It is meant to be a bridge for people.

So, I came in, naively, when I started using IndieAuth.com and said…I want to do the same thing, but I don’t want to log in using GitHub…I want to log in using my WordPress credentials. So, in 2018, I learned enough to write an IndieAuth endpoint for WordPress. So, you can, instead of putting indieauth.com as your provider, install a WordPress plugin and your site will become a provider.

Try to login with your URL and it will redirect to letting you login with WordPress, then issue credentials to the client in the form of a token that can be revoked from the WordPress admin.

But people continue to see IndieAuth as logging into other websites via IndieAuth.com and therefore via GitHub…that can certainly be a service and a thing you can do. However, that’s not IndieAuth.

So, going forward, I’ve decided that I’ll be disabling the code from the IndieWeb WordPress plugin that allows you to use IndieAuth.com in favor of the built-in solution. Those who want to use an external service will still be able to do so, but this will be an ‘expert’ feature. Because enabling a plugin and it just working is what most people want.

And if it doesn’t work, please report the bug and we’ll fix it.

Planning out the Next Generation of Post Kinds

I’ve been working on the Post Kinds plugin for several years now. It allows the enhancement of WordPress posts into the Indieweb types of posts.

But in the current environment, the question I keep getting asked is: When will it support Gutenberg, the WordPress block editor?

This is something of a problem for me as I do not know how this would work. I really need to talk it out with someone more embedded in the Gutenberg world and see what ideas come.

So, to prepare for that, I need to make a major shift in the way I have Post Kinds set up to disconnect further what happens on the frontend to what happens on the backend. And since Gutenberg uses the REST API to get data, I need to add an endpoint to work with this.

So, there are a few experiments in this regard I am working on. In a previous version of Post Kinds, I switched from storing metadata about a photo in the photo post to storing it in the attachment. WordPress uses a custom post type for attachments to store information about video, audio, images, etc.

This means you can edit the attachment, and separately attach it to the post. So, one of the first things I want to do is enhance that, and add the ‘citation’ post type I’ve been contemplating.

Loosely inspired by the old WordPress links manager, this would store the metadata for individual URLs. It would be necessary as I want to be able to turn my website into a Pinboard-esque bookmark archive. Not every bookmark is to be shared in my feed, or even public.

So, ones that are shared would be attached to a post, similar to the way attachments are. But otherwise could be addressed as simply bookmarks.

Over the last week, I experimented heavily with a simple fields API to generate the forms fields by configuring an array. The idea here is to define the fields I need for any given post kind, and therefore generate them automatically, rather thanm writing templates.

So, both of these ideas are elements of what I want to build. The challenge is the UI to bring these pieces together. It is going to require some trial and error on my part to get it right.

Right now, after having merged in the Fields file, which I will eventually hook up to the form interfaces, I will be finishing the Citation Edit UI, and then seeing how I can link that to posts, taking the attachment model.

Then working on how to properly save and edit the combined post.

Thoughts?

Micropub for WordPress 2.1.0 Released

Micropub version 2.1.0 for WordPress was released. It contains no exciting new features as Micropub is fairly stable.

  • Micropub now checks WordPress capabilities more effectively. It will now throw an error if the user tries to edit a post authored by another user if they do not have that permission, for example.
  • Default Titles and captions weren’t being set for images when uploaded.
  • Fix a variety of timezone related bugs
  • Added support for the proposed draft scope. If the token has this scope and not the create scope, then it will only allow posts to be created as drafts.
  • Return the created URL in the Micropub response to support an automation use case.
  • Add filters to allow a custom post type or custom taxonomy to be set during creation.
  • No longer support the ‘post’ scope as permission to do anything. It will map to a combination of create and update.
  • Misc unit test tweaks for consistency.
Version 4.2.0 of Syndication Links was released today. This version refactors the site to icon mapping to try to improve accuracy. It also includes:

  •  Adds support for generating a custom feed to syndicate to Micro.blog and pull back the links to your posts
  • Add support for Bridgy Mastodon, courtesy of Charlotte Allen
  • Visual enhancements to the Syndication metabox in the post editor, courtesy of Caspar Hübinger

And the usual miscellaneous bug fixes

 

Playing with Image Tagging for Workflow

At IndieWebCamp NYC this weekend, the discussion came up regarding workflow of saving various things. Media was one of them, particularly photos.

Now, in more recent years, my photography has moved from a digital camera to a cell phone. I just recently bought tools to better hold my cell phone when taking a lot of pictures and I enjoy taking pictures.

One of the issues has been with documenting the photos. Not everyone will I post online. Photos taken digitally will have a date, details of the hardware, and if with a cell phone, a location embedded(if that is turned on), but not notes on what it is.

I already had a rather complex app that allowed me to edit all embedded metadata on photos, but I found a simple one that only allowed editing 4 fields…namely title and author, and included bulk editing options.

I then, at the camp, fixed some issues that would pull that information out of a photo when uploaded and use it as the title and/or caption in the backend.

The problem is multi-fold. For one, will I actually label photos on my phone? Secondly, I upload to multiple places automatically…I’m not sure if the changes would sync before or after the automatic upload.

Glad that I am trying this, but more to figure out before it works.

Using the Last Seen Function in Simple Location

One of the features in Simple Location that doesn’t get much notice is the Last Seen functionality.

Simple Location adds a section to your WordPress user profile called Last Reported Location. It allows you to set the last reported location for a given user.  It reports latitude, longitude, altitude, and whether or not the location is public, private, or protected.

In the Simple Location Settings, you can set this to update each time you publish a post if the location isn’t set as private. So it would reflect the last post you made.

This feature can be used in one of two ways. You can add the Last Seen Widget to your page and display the last place you were seen. Alternatively, this can work in reverse. You can set it so your posts will set post location from the last seen location of the author.

But, what use is that if the only way a to update the Last Seen setting is to set it from a post(creating an endless loop) or to set it manually? If you always want to always set a default location, this can be an option.

However, that doesn’t work for me. So, I built a way to update your location from an outside service.

First, you need an IndieAuth token. If you installed the IndieAuth plugin, you can get one manually under Users->Manage Token.

curl -i -H 'Authorization: Bearer FAKETOKEN' -d "latitude=30&longitude=-115&visibility=public" "https://www.example.com/wp-json/sloc_geo/1.0/user"

Here is an example of updating your location via a curl command line command. It figures out which user based on the user of the token you created.

The parameters currently used are latitude, longitude, altitude(will be automatically derived if not present), and visibility(public, private, protected).

If you are successful, it will return ‘Updated’ and automatically lookup the name of the location you are at.

So, what can you do with this feature? Keeping in mind the day, though I call today Tuesday…Let’s say hypothetically you are in the package delivery business and you want to share your location with the people who are eagerly awaiting your deliveries. You could use this to send your location from your phone to your website to keep the display updated.

Alternatively, if you don’t trust the browser on your computer to know where you are, you could rig up a shortcut on your phone to update the location so it would be accurate if you post, for example on Android with Tasker.

There is more that is needed to enhance this feature. On my list for future is Geofencing…the idea of zones inside which the location would either be set to private or display a generic ‘Home’ or ‘Work’ etc. I already have the code to calculate this, but haven’t figured out how the UI would look. This would allow much more granular controls than the global privacy default.

Episode 12()

In the latest episode of An Indieweb Podcast, Chris Aldrich joined me for a late night(for me at least) discussion of Gutenberg, the new WordPress editor, and the usual project talk. I felt I was a bit off due the lateness of the hour, but I still enjoyed the conversation.

Syndication Links 4.0.0 Released

Today, from my hotel room in Berlin, Germany, where I am preparing to attend Indiewebcamp Berlin, my first European Indiewebcamp, I released Syndication Links 4.0.0.

The major version number change is because in this version, Syndication Links takes on a new role. As promised previously, I’ve built new syndication code and added supported for Bridgy and Indienews, which both uses Webmentions to trigger a syndication action. This is disabled by default.

As my first live use outside of testing, I’m using the plugin to send this post to Indienews and Twitter(via Bridgy).

The new code adds the concept of a syndication provider, which, when registered, adds the provider as a syndication target for Micropub clients as well as adds it to the WordPress classic editor as a series of checkboxes, er postone for each service.

The Bridgy Publish plugin I announced deprecation on had additional options on a per post and a global level. While the global settings will be coming back in a future version, I likely will not bring back the per-post settings.

Instead, I’d like to add more intelligence behind these decisions, based on other properties of the post. A checkbox is all you should need. The same with auto-syndication. If you decide you want everything to go to Twitter or some site, it shouldn’t check the box…there shouldn’t be a box at all. It should just go, even if there are some more parameters to make that decision…type of post, etc.

So, you are either all in, or in on a per post basis.

I look forward to feedback. This is only the beginning. I hope to do what I did for displaying syndication links, and interface with existing plugins in addition to writing my own integrations.

Simple Location Version 3.4.0 Released

I released a new version of Simple Location this evening. I had to start this project, moving it up my list of things to do because Nominatim started blocking me. I used Nominatim for looking up addresses from coordinates.

Because of that, I completely rewrote the system that registers new location providers so I could more easily create new ones.

  • The Nominatim provider has been switched to now use the Nominatim API provided by MapQuest(Yes, they are still around).
  • You can now use Bing and Google to lookup addresses from coordinates
  • Bing, Google, and Mapquest will now fill in elevation/altitude data on posts if not supplied during lookup, based on their APIs for this.
  • Altitude will display if it is over 500 meters. So, right now, basically if I post on a plane.
  • Location visibility, which is a feature now built into at least one Micropub client, has been enhanced in here. It should work more reliably now.
  • Mapquest and HERE are new static map providers.
  • The conflict with the Jetpack plugin, which added location services in 2017 unknown to me, has been resolved.  If you activate this plugin, it unloads the conflicting Jetpack module.
  • If there is no address to display, it will now display the coordinates.
  • Dark Sky is now an alternate weather provider. I was going to add Weather Underground as well, but they apparently shut down their API.
  • When publishing using Micropub, if coordinates are provided, it automatically generates a display address if none is provided and stores the current weather conditions.
  • The default location visibility checkbox now offers any three of the visibility options… Private, Public, or Protected. Protected is show the display address but not the map or the coordinates.

What’s next for this plugin? Well, better logic around location visibility. Right now, if you do not set it, it goes to a global default.

So, I’d like to include geofencing. That would be a list of zones. Zones would be a location with a radius around it that if you are inside, it would automatically set to protected and/or replace the address with a generic one. For example, if you are within 50 meters of home, it would always use a pre-identified location of ‘Home’ and/or default to a city level description.

The plugin supports Location providers other than the browser. This means that the plugin could query a server to get the current location of the user.

However, there aren’t any I’ve implemented yet really. But this would allow me to query an API to get my location, which has a lot of potential in future, especially if I want to look up my historical location.

For example, I’ve let Google store some of my location history since March of 2013. There is no API I know of to poll the data, but you can export it. I would just have to find a place to import it to. 6 years, 50mb of data. If I had some way to load it up, query it by date and time, I would be able to add location to all posts and photos that didn’t have it, based on the location of my phone at the time the post was made.

I already have a program on my phone that sends my location periodically to my home automation system, but I’d have to try something different to store historical data. There are several options for this. I’m tempted to write something into WordPress, as I have a tendency to build things into my website. Not sure if there is an off the shelf project to suit my needs, though I’ve looked at Aaron Parecki’s Compass. I could build similar functionality into my site to accommodate this, but I think I need something that both my house and my website can query.

Either way, look to see me testing this on my upcoming trip to Indiewebcamp Berlin.

 

 

 

Thoughts About Assertion Workflows

This is a preliminary technical workflow proposal for assertions, which would be needed for badges, endorsements, and other ideas. It is based on thoughts that I had listening to the badges session at Indiewebcamp NYC 2018.

Scenario 1: Individual creates criteria and wants to assert that other individual has achieved said criteria. Example: Professor wants to certify students completed coursework.

  • Professor Posts Criteria for Each Achievement as a unique page (A).
  • Student completes assignment as a post (B).
  • Professor Posts Badge/Assertion/Endorsement post on their website as an h-review, with a p-item property to student’s URL (B). Would need a new or existing property to represent the relationship to the original assertion (A). Suggest u-assert and u-assert-of?j Can use u-in-reply-to possibly.

Scenario 2: Individual creates assertion post and solicits others to endorse that statement as factual.

  • Individual makes a post to their site(h-resume for references on a resume, not sure what to request endorsement of a statement? p-assert with a nested h-item?) and invites other individuals(using existing invitee property used for RSVPs?) to endorse or assert it. Criteria might be included for achievement.
  • Others create ‘assertion’ posts on their site(assert-of) and send webmentions, which would cause the post to be updated to note that it had been achieved.

Existing microformats for h-resume and h-review seem to allow additional context.

  • Education
  • Experience
  • Skill
  • Rating
  • Best
  • Worst

Brainstorming on the Indieweb wiki under assertion.

Replied to Stepping back from POSSE by Ben WerdmüllerBen Werdmüller (Ben Werdmüller)

I’m also going to make a strong argument in the open source Known community that syndication should be limited to webhooks going forward. In other words, third parties will be able to create microservices with a standard API, which your Known or other indieweb-compatible site will be able to connect to. You could click a button to notify those services (or have your site do it automatically). But any kind of API maintenance would be taken out of the core code or official plugins. Not only is life too short, but it’s long past time to stop building code on top of centralized silos of content.

I have to agree with Ben. The Micropub plugin for WordPress triggers a WordPress hook based on syndication targets and I’m working on a compatible system for the Post Editor, so that the UI doesn’t need to know how the item is POSSEd.

This is because I also don’t want to deal with silo interfaces most of the time.

Brainstorming on Implementing Vouch, Following and Blogrolls

Vouch is an extension to the webmention protocol. Webmentions usually have two parameters…source and target. Target is the URL on your website  that the Source URL is linking to.

The vouch parameter is a third URL to help the target determine whether or not they should accept the webmention. This should block automated spam and aid in moderation.

Several people have implemented receiving vouches. It is relatively easy  to look at a vouch URL and see if it links to a third-party who you have approved of in the past.  While there are more advanced things you can do, that is the basic summary of the protocol.

The harder part, and less implemented by others is sending of vouches. Where do you find people who have been approved by people you have approved of? It would really help if we had some more discussion on this.

So, at the Indieweb Summit, we talked about this a bit, after which I implemented a primitive Vouch receiver. My solution was to use a manually curated domain whitelist that I’d previously built as my source for acceptable domains.

There are some suggestions on where to get this list. Several people generate a list from referrers. This sent me down the road of looking as to whether I’d want to implement refbacks to add more mentions to my website…except there is a lot of noise. Refbacks are basically the same as webmentions, except the source is gleaned from the  referrer header that sites send when a page is accessed.

Even if I  have a list of sites that I approve of, I would have to crawl them to find links from them to other sites. So, I think we should all help each other out on this.

That means we need to post our list of approved domains somewhere on our site. That used to be quite popular. It was called a Blogroll. It was sites you read, followed, or recommended. There are other terms for it. But, this is a perfect place to get a nice list, and if we publish them, then we can help the Vouch cause.

But the problem is, how do you tell a Vouch receiver where your list is. There are some brainstorming items about blogrolls and following/follower lists

  • Follower lists marked up with rel=”follower” or rel=”following”
  • Contact lists marked up with rel=”contact”
  • Follow Posts marked up with u-follow-of

Follow posts would create an h-feed of follow posts that could be used to generate a list. You can have a specific page on your website, but there isn’t a way to indicate this to someone looking for it.

There is rel-directory, which is the reverse direction. It indicates that the link is to a directory in which the current page is listed. What we seem to be missing is a property that says that a page is a feed of followers that can be placed inside an h-card or on a home page.

u-follow-of is a proposed property that indicates that an h-entry is considered a follow post, which is a post indicating you have followed someone, then a feed of follow posts could be parsed and read by a reader. If you add in the XFN relationships to that, you can build even more detail.

The reverse relationship would, in theory, be u-follow, which would be a URL to the follow post of the current URL(the thing being followed).

Feeds are identicated by rel=”feed” to link from your homepage to those feeds. But there is a lack of indicating what type of feed it is, such as rel=”blogroll” or rel=”following”.  I’m not sure, and need more discussion about what to use for this.

But, this has the ability to solve a lot of problems. Imagine I…

  • Post Follow posts when I follow someone
  • Use this to generate a blogroll/followers list
  • Send webmentions when I follow someone so they can build relationships
  • Use that list as a vouch list. Use other people’s blogrolls/followers lists as a means to generate vouch lists…which reduces the implementation cost of Vouch.

Needs work, but suddenly I want to do Follow posts.