I've been thinking about how to do hCards. I built a small example version which I commited to the CVS. It builds up the XHTML using JavaScript and works well as an example, but is very bulky and I can see it becoming very limited.
I'm thinking a better option may be to use the existing Profiles module to build hCards. With Profiles, you can create custom profile fields, and my idea is to build a custom hCard module that takes these fields and allows you to map them to defined hCard fields (i.e. profile-firstname = given-name, profile-company = org, etc) which this module then outputs in the correct format.
I think this would save any need to have a seperate database to store the hCard data, instead hCard would just be built from the profile database each time it is needed. Then there could be an option in node creation to embed a hCard into the post and just creates this from the posters profile. But the question is do we make the call each time, or does it generate the XHTML and embed it in the post?
I would also like to have a hcard embed tag that you can attach to any element, and using jQuery and DOM the hCard could be added to the tag (something like class="embed-hcard"). So for example, if I was linking to Eric Meyers blog, I would do:
<a href="http://meyerweb.com/" class="embed-hcard">Eric Meyer</a>jQuery would then remove the link and replace it with a hCard.
So what do you think? Any suggestions, any ideas that could move this along?

Comments
Had a think about it
Hey folks,
I had a think about this last night, and put paper to pen to get my ideas out, so this post is just taken from that, so some of this might be rubbish, or not make sense, but generally I think it'll be a good talking point.
What I would like to do with the first module, hCard is in the admin screen is to connect to the profile table and read the fields and their types. This then generates a select list of all the fields on the left hand column. On the right, there is another select box which related directly to XHTML fields in the hCard (i.e. fn, url, org, etc) and a Assign button. This is an interface built with jQuery, so when you click assign the page does not refresh, but instead a new row apperars. This allows you to build up a hCard schema from the profile module fields.
Below the form area is 2 live preview areas - one that displays a CSS-styled hCard and another that shows the raw XHTML that can be copied.
I think it would also be worth adding in a XFN generator so a rel tag can be added to the hCard. Any hCards that have rel="me" are assigned to the user profile and this adds other features.
In the node creation page, there should be a new option at the bottom next to the publishing option checkboxes that offer "Embed my hCard in this node". This would then replace the posters name in the node with a hCard.
For node creation, there should be a parser that can read the post before submitting it to the database, and looks for any object with class="hcard". When the parser checks this, it reads the text between the tags and if this matches any hCard's in the database it replaces the code. If there is no hCard in the database that matches this, i'm not sure what we do here? Do we just specify that if there is not one, it will have to written manually?
Anyway, thats my ideas so far. Anyone want to pick them apart?
Tane Piper
Digital Spaghetti
http://digitalspaghetti.me.uk
VCard module
The vCard module already inserts hCard based on profile data. Have a look and see if that works for you. Some embeddable filters are a good idea. I'd love to see an easy way to include links to local site users.
Have you seen this yet?
http://drupal.org/node/79227#comment-127815
I posted this link on another thread. I just thought that you'd want to know that you're on the right track regarding the profile module!
Rick
Do hCards at theme layer?
Over the summer, I was asked to implement a prototype of LiveClipboard, which provides the ability to copy microformatted content into the desktop clipboard (think right-click, copy, paste, etc). The LiveClipboard examples included an option to generate contacts in hCard format. The team at Microsoft R&D who developed this tool used an hCard.js script (and put it under Creative Commons), which made it rather easy for me to put hCard generation into a custom user_profile.tpl.php file in Drupal.
It would probably be relatively straightforward to extend this method. The only limitation from my work is that I resorted to a quick hack to get the functionality out the door: I hardcoded the fields from my user profiles into the hCard.js library. I suppose that this javascript could be generated dynamically, or perhaps this static .js file could be built (and rebuilt) by a module that tracks changes to profile fields.
By way of background, it is important to note that I set my page.tpl.php to route all user profile calls to page-user.tpl.php file. I then created a user_profile.tpl.php file, which (when inserted into page-user.tpl.php) outputs the following code:
<head>...
<link href="/sites/example/modules/vcard/css/webClipControl.css" type="text/css" rel="stylesheet" />
<script src="/sites/example//modules/vcard/js/script.js" type="text/javascript"></script>
<script src="/sites/example//modules/vcard/js/hCard.js" type="text/javascript"></script>
...
</head>
<body>
...
<script type="text/javascript">
function MicroFormatObjectBinding(displayDiv, microFormatObject, inactiveStyleClassName, activeStyleClassName)
{
var webClip;
var self = this;
this.updateDisplayAndWebClipData = function()
{
webClip = new LiveClipboardContent();
displayDiv.innerHTML = microFormatObject.HTML;
webClip.data.formats[0] = new DataFormat();
webClip.data.formats[0].type = microFormatObject.formatType;
webClip.data.formats[0].contentType = "application/xhtml+xml";
webClip.data.formats[0].items = new Array(1);
webClip.data.formats[0].items[0] = new DataItem();
webClip.data.formats[0].items[0].xmlData = microFormatObject.xmlData;
}
this.onActive = function()
{
if (displayDiv.childNodes[0] && (displayDiv.childNodes[0].className == microFormatObject.formatRootClassName))
displayDiv.className = activeStyleClassName;
}
this.onInactive = function()
{
displayDiv.className = inactiveStyleClassName;
}
this.onCopy = function()
{
return webClip;
}
this.onPaste = function(clipData)
{
// the user profile view has not paste functionality, as it does not allow editing
for (var i = 0; i < clipData.data.formats.length; i++)
{
if ((clipData.data.formats[i].contentType = "application/xhtml+xml") && (clipData.data.formats[i].type == microFormatObject.formatType) && (clipData.data.formats[i].items.length > 0) && (clipData.data.formats[i].items[0].xmlData))
{
//var hCardXmlString = clipData.data.formats[i].items[0].data;
//microFormatObject.initFromXmlString(hCardXmlString);
microFormatObject.initFromXml(clipData.data.formats[i].items[0].xmlData);
self.updateDisplayAndWebClipData();
// For now, just look at the first microFormatObject found.
return;
}
}
//No matching formats found -- this could mean cut/delete -- so clear the associated data.
microFormatObject.clearProps();
self.updateDisplayAndWebClipData();
}
self.updateDisplayAndWebClipData();
}
// Contacts
var hCard1 = new HCard(<?php print "'".check_plain($user->profile_name_given) ."','". check_plain($user->profile_name_family)."','','', null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,";?><?php if($user->picture):print"'/".$user->picture."'," ?><?php else:print 'null,';?><?php endif;?><?php if($user->profile_job_title):?><?php print "'".$user->profile_job_title."',null,"?><?php else:?><?php print "null,null,"?><?php endif;?><?php if($user->profile_org_name):?><?php print "'".$user->profile_org_name ."'"?><?php else:?><?php print "null"?><?php endif;?>);
var contactBinding1 = new MicroFormatObjectBinding(document.getElementById("contactHcard1"), hCard1, "hcardContainer", "hcardContainer selected");
var clipBoardControl1 = new WebClip(document.getElementById("webClipControl1"), contactBinding1.onCopy, contactBinding1.onPaste, contactBinding1.onActive, contactBinding1.onInactive);
</script>
Profile pages / user list
The profile pages themselves, as well as the user list, need to be hCards