<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>techbits.de</title>
	<atom:link href="http://www.techbits.de/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.techbits.de</link>
	<description>thoughts on hardware, software, development and tech news</description>
	<lastBuildDate>Mon, 02 Apr 2012 20:22:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Setting up an LDAP directory server for Alfresco development</title>
		<link>http://www.techbits.de/2012/04/02/setting-up-a-development-ldap-server-for-alfresco/</link>
		<comments>http://www.techbits.de/2012/04/02/setting-up-a-development-ldap-server-for-alfresco/#comments</comments>
		<pubDate>Mon, 02 Apr 2012 20:15:21 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[development]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=414</guid>
		<description><![CDATA[The following video explains how to set up the Apache Directory Studio, create users and groups and import them into Alfresco. This allows you to run your own LDAP server for testing and development purposes for Alfresco. Here is the &#8230; <a href="http://www.techbits.de/2012/04/02/setting-up-a-development-ldap-server-for-alfresco/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The following video explains how to set up the <a href="http://directory.apache.org/">Apache Directory Studio</a>, create users and groups and import them into Alfresco. This allows you to run your own LDAP server for testing and development purposes for Alfresco.</p>
<p><iframe width="584" height="329" src="http://www.youtube.com/embed/xJr1hJVo2So?fs=1&#038;feature=oembed" frameborder="0" allowfullscreen></iframe></p>
<p>Here is the alfresco-global.properties configuration that I have used for the LDAP sync:</p>
<pre>###############################################################################
# The default authentication chain
# To configure external authentication subsystems see:
# http://wiki.alfresco.com/wiki/Alfresco_Authentication_Subsystems
#-------------

######AUTHENTIFICATION CHAIN####
authentication.chain=alfrescoNtlm1:alfrescoNtlm,ldap1:ldap

######SUBSYSTEM AUTHENTIFICATION####

### SIMPLE AUTHENTIFICATION ###
ldap.authentication.java.naming.security.authentication=simple
ldap.authentication.userNameFormat=uid=%s,ou=users,ou=system

ldap.authentication.active=true
ldap.authentication.java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
ldap.authentication.java.naming.provider.url=ldap://localhost:10389
ldap.authentication.allowGuestLogin=true

#####################################
###### SUBSYSTEM SYNCHRONISATION ####
#####################################

ldap.synchronization.active=true

ldap.synchronization.java.naming.security.principal=uid=admin,ou=system
ldap.synchronization.java.naming.security.credentials=secret
ldap.synchronization.queryBatchSize=1000

ldap.synchronization.groupQuery=(objectclass=groupOfNames)
ldap.synchronization.groupDifferentialQuery=(&amp;(objectclass=groupOfNames)(!(modifyTimestamp&lt;\={0})))

ldap.synchronization.personQuery=(objectclass=inetOrgPerson)
ldap.synchronization.personDifferentialQuery=(&amp;(objectclass=inetOrgPerson)(!(modifyTimestamp&lt;\={0})))

ldap.synchronization.groupSearchBase=ou=groups,ou=system
ldap.synchronization.userSearchBase=ou=users,ou=system

ldap.synchronization.modifyTimestampAttributeName=modifyTimestamp
ldap.synchronization.timestampFormat=yyyyMMddHHmmss'Z'

################# ATTRIBUTE MAPPING #############
#### mapping to unique username in username attribute###
ldap.synchronization.userIdAttributeName=uid
#ldap.synchronization.userLastNameAttributeName=cn
#ldap.synchronization.userEmailAttributeName=mail
#ldap.synchronization.userFirstNameAttributeName=givenName
ldap.synchronization.groupIdAttributeName=cn
#ldap.synchronization.groupType=
#ldap.synchronization.personType=
ldap.synchronization.groupMemberAttributeName=member
#ldap.synchronization.userOrganizationalIdAttributeName=company

ldap.synchronization.enableProgressEstimation=true</pre>
<pre>####################################
###### SYNCHRONISATION SETTINGS ####
####################################

synchronization.autoCreatePeopleOnLogin=false
#synchronization.import.group.clearAllChildren=true
# full sync or only changes?
synchronization.synchronizeChangesOnly=false

# to sync on each alfresco startup
synchronization.syncOnStartup=true
synchronization.syncWhenMissingPeopleLogIn=false

### DONT USE UNIX CRON EXPRESSION- USE QUARTZ CRON EXPRESSIONS!!!
### look here http://www.quartz-scheduler.org/docs/tutorials/crontrigger.html
### synchronisation starts every 15 minutes!
synchronization.import.cron=0 0/15 * * * ?</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2012/04/02/setting-up-a-development-ldap-server-for-alfresco/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using the Javascript Console: Creating users with avatar images</title>
		<link>http://www.techbits.de/2011/12/02/using-the-javascript-console-creating-users-with-avatar-images/</link>
		<comments>http://www.techbits.de/2011/12/02/using-the-javascript-console-creating-users-with-avatar-images/#comments</comments>
		<pubDate>Fri, 02 Dec 2011 22:51:07 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[alfresco]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JavascriptConsole]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=387</guid>
		<description><![CDATA[Automatically creating users has become easier with the new CSV user import features in Alfresco 4.0 or the Create Bulk Users extension from share-extras. If you want to be really flexible and set all the user properties or even set custom &#8230; <a href="http://www.techbits.de/2011/12/02/using-the-javascript-console-creating-users-with-avatar-images/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Automatically creating users has become easier with the new CSV user import features in Alfresco 4.0 or the <a href="http://code.google.com/p/share-extras/wiki/CreateBulkUsers">Create Bulk Users extension from share-extras</a>. If you want to be really flexible and set all the user properties or even set custom properties, using the Javascript API to create users can still be very useful. That&#8217;s why I&#8217;ll show you how to create users using the Javascript Console.</p>
<p><span id="more-387"></span></p>
<p>Let&#8217;s start with a short video preview on how the user and avatar creation works.</p>
<p><iframe width="584" height="438" src="http://www.youtube.com/embed/mDYvRYiYel0?fs=1&#038;feature=oembed" frameborder="0" allowfullscreen></iframe></p>
<h1>Creating users</h1>
<p>Here is a function that creates a single user using the <strong>createPerson</strong> function of the global <strong>people</strong> object. The additional properties location and jobtitle are also filled here for the user. The cm:person object has several more properties to offer. To see a list of all properties available have a look at the <a href="http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/root/projects/repository/config/alfresco/model/contentModel.xml">cm:person type in the contentModel.xml</a>.</p>
<pre>function createUser(username, firstname, lastname, email, location, 
  jobtitle) {

  var password = username; // use for testing only!

  var p = people.createPerson(username, firstname, lastname, email,
    password, true);

  p.properties["cm:location"] = location;
  p.properties["cm:jobtitle"] = jobtitle;
  p.save();
}</pre>
<p>It is useful to have this custom createUser function to easily create multiple users. Here is a list of some users we want to add to Alfresco.</p>
<pre>createUser("homer", "Homer", "Simpson", "h.simpson@springfield.fox", 
    "Springfield", "Nuclear Safety Inspector");

createUser("marge", "Marge", "Simpson", "m.simpson@springfield.fox",
    "Springfield", "Housewife");

createUser("bart",  "Bart",  "Simpson", "b.simpson@springfield.fox",
    "Springfield", "Kid");

createUser("lisa",  "Lisa",  "Simpson", "l.simpson@springfield.fox",
    "Springfield", "Kid");

createUser("maggie","Maggie","Simpson",
    "maggie.simpson@springfield.fox", "Springfield", "Baby");</pre>
<h1>Assigning avatar images</h1>
<p>Now we have created a few users. The only thing that is missing are the avatar images for each of the users. Wouldn&#8217;t it be great to just throw a bunch of images in a repository folder and have them automatically assigned to each user without having to log in as each of these users and assign an avatar image manually?</p>
<pre>for each (imageNode in space.children) {
  var name = "" + imageNode.name;  // convert to javascript string
  name = name.replace(/\..*/, ""); // remove file extension

  var user = people.getPerson(name);

  if (user) {
    var avatarAssoc = user.assocs["cm:avatar"];

    if (avatarAssoc) {
      var currentAvatar = avatarAssoc[0];

      if ((""+imageNode.nodeRef) != (""+currentAvatar.nodeRef)) {
        logger.log("changing avatar for " + name + " to " +
            imageNode.displayPath + "/" + imageNode.name);
        user.removeAssociation(currentAvatar, "cm:avatar");
        user.createAssociation(imageNode, "cm:avatar");
      }
      else {
        logger.log("no change for user " + name);
      }
    }
    else {
        logger.log("setting new avatar for " + name + " to " +
            imageNode.displayPath + "/" + imageNode.name);
        user.createAssociation(imageNode, "cm:avatar");
    }
  }
}</pre>
<p>This script does exactly that. It assumes that the <strong>space</strong> variable points to a repository folder that contains an avatar image for each user. Here is how you use it.</p>
<ul>
<li>Create a folder somewhere in the repository, e.g. /Data Dictionary/<strong>User Images</strong> would be good name to use.</li>
<li>Upload your avatar images to that folder. Square aspect ratio would be best but image resolution doesn&#8217;t matter.</li>
<li>You avatar images should be named by username, e.g. &#8220;<strong>homer.jpg</strong>&#8221; or &#8220;marge.png&#8221; if you have the users &#8220;<strong>homer</strong>&#8221; and &#8220;marge&#8221;.</li>
<li>Select the &#8220;User Images&#8221; folder as space in the Javascript Console and run the script you see above.</li>
</ul>
<h1>A few things to note</h1>
<div>
<ul>
<li>The avatar image is stored with a <strong>cm:avatar</strong> association on each cm:person object that means that the image can be anywhere in the repository.</li>
<li>The avatar image that is assigned by the cm:avatar association will be converted into a <strong>64&#215;64 pixel thumbnail</strong> using the rendition service internally. This means you can use an image of any format and size that the rendition service can convert.</li>
<li>There is this odd comparison in the script:
<pre>(""+imageNode.nodeRef) != (""+currentAvatar.nodeRef)</pre>
<p><strong>Comparing nodeRefs</strong> can be really tricky in the Alfresco Javascript API, because they are Java objects. Here nodeRefs are converted into Javascript strings which can be compared with == or !=. Another way would be to use the Java equals() method like this:</p>
<pre>imageNode.nodeRef.equals(currentAvatar.nodeRef)</pre>
<p>but what you should not do, is directly comparing nodeRefs with == it won&#8217;t work:</p>
<pre>imageNode.nodeRef == currentAvatar.nodeRef</pre>
</li>
<li>Of course you can use the avatar assignment script regardless of how you created the users. Even if the users have been imported with the LDAP synchronization you can still have the repository folder with the user images an have them assigned automatically. You could even <strong>create a rule</strong> that runs this script as soon as a new image is uploaded to the &#8220;User Images&#8221; folder.</li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2011/12/02/using-the-javascript-console-creating-users-with-avatar-images/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using the Javascript Console: Permission reporting</title>
		<link>http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/</link>
		<comments>http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/#comments</comments>
		<pubDate>Sun, 06 Nov 2011 17:53:00 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[alfresco]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JavascriptConsole]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=364</guid>
		<description><![CDATA[I just came across an old question in the german Alfresco forum on how to create a report of permissions on all nodes in the repository. I thought to me this would be really easy to do with the Javascript &#8230; <a href="http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I just came across an old question in the german Alfresco forum on how to create <a href="https://forums.alfresco.com/de/viewtopic.php?f=8&amp;t=593">a report of permissions on all nodes in the repository</a>. I thought to me this would be really easy to do with the <a href="http://code.google.com/p/share-extras/wiki/JavascriptConsole">Javascript Console</a> and went ahead creating this script:<span id="more-364"></span></p>
<h1>The implementation</h1>
<pre>recurse(companyhome, function(node) {
  for each(permission in node.fullPermissions) {
    if (/;DIRECT$/.test(permission)) {
      logger.log(node.displayPath + "/" + node.name + ";" + permission);
    }
  }
});</pre>
<p>There are a few interesting things going on here.</p>
<ul>
<li>It uses a special <strong>recurse()</strong> function that iterates the repository recursively. This function has been built into the Javascript Console for some time but is still undocumented because I am hesitant to decide if it is ok to change or add to the Javascript API. Scripts can not be used outside/without of the Javascript Console anymore when using those extensions.</li>
<li>The iteration starts with <strong>companyhome</strong> but you could use the <strong>space</strong> variable as well and select any folder as a starting point.</li>
<li>I learned there is a special property called <strong>node.fullpermissions</strong> which is undocumented in the <a href="http://wiki.alfresco.com/wiki/4.0_JavaScript_API">Alfresco Javascript API </a>but is needed here because it returns information if a permission is inherited or not (the documented node.permissions does not tell this).</li>
<li>The script filters out any permissions that are INHERITED and only shows the DIRECT permissions to make the result list not too huge.</li>
</ul>
<h1>The result</h1>
<div>The result of the script is a semicolon separated text output that can be imported in Excel for example, for further analysis and reporting.</div>
<div>
<pre>/Company Home/Data Dictionary;ALLOWED;GROUP_EVERYONE;Consumer;DIRECT
/Company Home/Data Dictionary/RSS Templates;ALLOWED;guest;Consumer;DIRECT
/Company Home/Data Dictionary/Saved Searches;ALLOWED;GROUP_EVERYONE;Contributor;DIRECT
/Company Home/Guest Home;ALLOWED;guest;Consumer;DIRECT
/Company Home/Guest Home;ALLOWED;GROUP_EVERYONE;Consumer;DIRECT
/Company Home/User Homes/abeecher;ALLOWED;abeecher;All;DIRECT
/Company Home/User Homes/abeecher;ALLOWED;ROLE_OWNER;All;DIRECT</pre>
<h1>Some background on recurse()</h1>
<p>The recurse() function is only available within the Javascript Console and can be used to recursively search through the Alfresco Repository. It is very versatile, check out the following examples.</p>
<p>You can generate an <strong>array of all nodes</strong> under a specific folder:</p>
<pre>var allNodes = recurse(space);</pre>
<p>This will return an array with ScriptNode objects (folders and documents).</p>
<p><a href="http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/jsconsole-recurse-1/" rel="attachment wp-att-369"><img class="aligncenter size-full wp-image-369" title="jsconsole-recurse-1" src="http://www.techbits.de/wp-content/uploads/2011/11/jsconsole-recurse-1.png" alt="" width="729" height="525" /></a></p>
<p>The second way to use the recurse function is using a <strong>callback function</strong> as we did in the initial script. The callback is called for each node. Internally node.children is used to get the child nodes and recurse deeper. if you <strong>return any objects</strong> from the processing function they will be added to an array and are returned from the recurse() function as the first example showed it. Using the callback function is probably the most useful way to use it:</p>
<pre>recurse(space, function(node) {
   logger.log(node.displayPath + "/" + node.name);
});</pre>
<p><a href="http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/jsconsole-recurse-2/" rel="attachment wp-att-370"><img class="aligncenter size-full wp-image-370" title="jsconsole-recurse-2" src="http://www.techbits.de/wp-content/uploads/2011/11/jsconsole-recurse-2.png" alt="" width="762" height="556" /></a></p>
<p>The third way allows to <strong>pass in options</strong> to change to depth of the recursion. Currently there is only the <strong>maxLevel</strong> option which is used in the following example to only iterate two levels deep:</p>
<pre>recurse(space, {
  process : function(node) {
    logger.log(node.displayPath + "/" + node.name);
  },
  maxlevel : 2
});</pre>
<p><a href="http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/jsconsole-recurse-3/" rel="attachment wp-att-371"><img class="aligncenter size-full wp-image-371" title="jsconsole-recurse-3" src="http://www.techbits.de/wp-content/uploads/2011/11/jsconsole-recurse-3.png" alt="" width="633" height="550" /></a></p>
<p>One thing you should <strong>keep in mind</strong> when running recursive Javascript code in Alfresco is that the whole script runs <strong>in one transaction</strong>. When iterating over a large repository and especially if you are updating nodes you might run into trouble with the transaction caches and maybe even database locking.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2011/11/06/using-the-javascript-console-permission-reporting/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Using the tag editor component in Alfresco 4.0</title>
		<link>http://www.techbits.de/2011/11/05/using-the-tag-editor-component-in-alfresco-4-0/</link>
		<comments>http://www.techbits.de/2011/11/05/using-the-tag-editor-component-in-alfresco-4-0/#comments</comments>
		<pubDate>Sat, 05 Nov 2011 11:42:03 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[alfresco]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[alfresco4]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=349</guid>
		<description><![CDATA[This is just a short post to note down what I found out about the Insitu tag editor component from Alfresco 4.0 and how to use it in your own code. I have not actually used it yet, so the &#8230; <a href="http://www.techbits.de/2011/11/05/using-the-tag-editor-component-in-alfresco-4-0/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is just a short post to note down what I found out about the Insitu tag editor component from Alfresco 4.0 and how to use it in your own code. I have not actually used it yet, so the code below is not complete nor working. It is just meant as a reference (for me).<span id="more-349"></span></p>
<p>You need to include the documentlibary.css the editors CSS:</p>
<pre>&lt;@link rel="stylesheet" type="text/css" href="${page.url.context}/res/components/documentlibrary/documentlist.css " /&gt;</pre>
<p>Then you need a DIV to create the editor:</p>
<pre>&lt;div id="${el}-tageditor"&gt;&lt;/div&gt;</pre>
<p>You can create a Insitu-Editor for tags from Javascript like this:</p>
<pre>var tageditor = Alfresco.util.createInsituEditor(this.id + "-tageditor",
{
	type: "tagEditor",
	nodeRef : "workspace://SpacesStore/79cced9b-947b-4833-bf03-5fb4660449d9",  <strong>// use nodeRef here</strong>
	name: "prop_cm_taggable",
	value: "", <strong>// here go the tags of the current node</strong>
	validations: [{
		type: Alfresco.forms.validation.nodeName,
		when: "keyup",
		message: this.msg("validation-hint.nodeName")
	}],
	title: this.msg("tip.insitu-tag"),
	errorMessage: this.msg("message.insitu-edit.tag.failure")
},
{
	 fn: this._insituCallback, <strong> // your callback</strong>
	 scope: this,
	 obj: record <strong>// your object</strong>
});</pre>
<p>To display the editor you call:</p>
<pre>tageditor.doShow();</pre>
<p>This displays the following tag editor you know from the document library:</p>
<p><a href="http://www.techbits.de/2011/11/05/using-the-tag-editor-component-in-alfresco-4-0/insitu-tag-editor/" rel="attachment wp-att-350"><img class="aligncenter size-full wp-image-350" title="insitu-tag-editor" src="http://www.techbits.de/wp-content/uploads/2011/11/insitu-tag-editor.png" alt="" width="430" height="58" /></a>What it does:</p>
<ul>
<li>Provide a textbox, a save and a cancel button (as text)</li>
<li>Suggest and auto complete tags as you enter them</li>
<li>Create new tags in the repository on save</li>
<li>Save the tags for the node (using the nodeRef you specified)</li>
<li>Call your code after the update in the repo</li>
</ul>
<p>What it does <strong>not</strong> do:</p>
<ul>
<li>It does not read the current tags. You have to pass them in using the value parameter.</li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2011/11/05/using-the-tag-editor-component-in-alfresco-4-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building a Google Plus inspired image gallery</title>
		<link>http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/</link>
		<comments>http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 21:06:44 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[alfresco]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=212</guid>
		<description><![CDATA[I recently built a Google Plus inspired image Gallery as an extension for the Alfresco ECM product which won first place in Alfresco developer challenge. Now I&#8217;d like to share my insights, explain how I built it and give you &#8230; <a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I recently built a Google Plus inspired <a href="http://code.google.com/p/fme-alfresco-extensions/wiki/GalleryPlusDashlet">image Gallery as an extension</a> for the <a href="http://www.alfresco.com/">Alfresco ECM</a> product which <a href="http://blogs.alfresco.com/wp/2011-dashlet-challenge-winner-gallery-plus-dashlet-by-florian-maul/">won first place in Alfresco developer challenge</a>. Now I&#8217;d like to share my insights, explain how I built it and give you a starting point to build your own galleries based on the great G+ gallery design.</p>
<div class="wp-caption alignnone" style="width: 609px"><a href="http://fme-alfresco-extensions.googlecode.com/svn/trunk/Gallery%20Plus%20Dashlet/documentation/gallery-plus-screenshot-01.jpg"><img title="Screenshot of Alfresco Gallery Plus extension" src="http://fme-alfresco-extensions.googlecode.com/svn/trunk/Gallery%20Plus%20Dashlet/documentation/gallery-plus-screenshot-01.jpg" alt="" width="599" height="472" /></a><p class="wp-caption-text">Screenshot of Alfresco Gallery Plus extension</p></div>
<h1><span id="more-212"></span>The G+ image grid disected</h1>
<p><span style="direction: ltr;">The main focus of this article will be on the image grid. Let&#8217;s start with a look on the features of the original G+ image grid to find out what it takes to recreate it. To illustrate the features I am using <a href="https://plus.google.com/photos/104987932455782713675/albums/posts">Thomas Hawk&#8217;s G+ gallery</a> which has some amazing images (check it out). </span></p>
<ol>
<li><span style="direction: ltr;"><strong>Justifed alignment</strong>: All images are displayed in rows and aligned to left and right borders. <a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/1-justified/" rel="attachment wp-att-228"><img class="aligncenter size-large wp-image-228" title="G+ justified image layout" src="http://www.techbits.de/wp-content/uploads/2011/10/1-justified-1024x624.jpg" alt="" width="584" height="355" /></a></span></li>
<li><span style="direction: ltr;"><strong>Even spacing</strong>: All Images are evenly spaced (6px spacing, i.e. 3px margin around each image).  </span></li>
<li><span style="direction: ltr;"><strong>Natural order</strong>: Images don&#8217;t have a special ordering to better fit in the grid. I first thought the perfect alignment was solved with some sort of <a href="http://en.wikipedia.org/wiki/Bin_packing_problem">bin packing algorithm</a>, but that is not the case und is not needed. Images are usually sorted by date from latest to oldest.</span></li>
<li><span style="direction: ltr;"><strong>Cropped thumbnails</strong>: To achieve the justified layout some portions of the images are hidden (cropped). When I first checked out the G+ gallery I saw most of the cropping done with HTML but now it looks like Google generates thumbnails in any arbitrary resolution or cropping on the server-side.<br />
<img class="alignnone" title="w300-h150-n-k" src="https://lh3.googleusercontent.com/-64_dH1I5GRE/TqbHWEnIlAI/AAAAAAAA1JA/uR_3xHnf1-Y/w300-h150-n-k/Do%2BI%2BLet%2BHer%2BSleep%2BOr%2BShould%2BI%2BWake%2BHer%2BUp.jpg" alt="" width="300" height="150" /></span>  <img class=" alignnone" title="w150-h150-n-k" src="https://lh3.googleusercontent.com/-64_dH1I5GRE/TqbHWEnIlAI/AAAAAAAA1JA/uR_3xHnf1-Y/w150-h150-n-k/Do%2BI%2BLet%2BHer%2BSleep%2BOr%2BShould%2BI%2BWake%2BHer%2BUp.jpg" alt="" width="150" height="150" /><br />
Both images have a similar URL which only differs in a part which specifies the resolution. The left image contains the path /w300-h150-n-k/ whereas the right image contains the path /w150-h150-n-k/ and therefore is cropped differently on the server side.</li>
<li><span style="direction: ltr;"><strong>Big title image</strong>: There is a &#8220;title&#8221; image or (album cover image) which spans two rows.  <a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/2-big_title/" rel="attachment wp-att-229"><img class="aligncenter size-medium wp-image-229" title="G+ big title image" src="http://www.techbits.de/wp-content/uploads/2011/10/2-big_title-300x267.jpg" alt="" width="300" height="267" /></a></span></li>
<li><span style="direction: ltr;"><strong>Comment badges</strong>: The number of comments is displayed on each image thumbnail. <a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/3-comment-badge/" rel="attachment wp-att-231"><img class="aligncenter size-medium wp-image-231" title="G+ comment badges" src="http://www.techbits.de/wp-content/uploads/2011/10/3-comment-badge-300x145.png" alt="" width="300" height="145" /></a></span></li>
<li><span style="direction: ltr;"><strong>Dynamic resolution</strong>: The size of the images depends on the width of the browser window. G+ uses small images for smaller browser windows and larger images for wider browser windows, i.e. the image resolution adapts to the screen size. This is espacially useful for mobile devices which have lower resultion than desktop browsers.  </span></li>
<li><span style="direction: ltr;"><strong>Window resize</strong>: The image sizes change and the grid images are re-aligned dynamically when the browser window is resized.  </span></li>
<li><span style="direction: ltr;"><strong>Dynamic row height</strong>: This is a really subtle feature: Depending on the dimensions of the images in one row (e.g. if a lot of images in portrait format) some rows are rendered bigger in height than others. This helps portrait shots not to become too small.<a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/4-dynamic_row/" rel="attachment wp-att-232"><img class="aligncenter size-large wp-image-232" title="G+ dynamic row height" src="http://www.techbits.de/wp-content/uploads/2011/10/4-dynamic_row-1024x597.jpg" alt="" width="584" height="340" /></a></span></li>
<li><span style="direction: ltr;"><strong>Popup Preview</strong>: When you hover over an image an animated popup appears and displays the image in a larger rendition.<a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/5-popup/" rel="attachment wp-att-237"><img class="aligncenter size-medium wp-image-237" title="G+ image popup" src="http://www.techbits.de/wp-content/uploads/2011/10/5-popup-300x298.jpg" alt="" width="300" height="298" /></a></span></li>
<li><span style="direction: ltr;"><strong>Infinite scroll</strong>: When you scroll to the bottom of the page more images are loaded automatically.</span></li>
</ol>
<p><span style="direction: ltr;">As far I have seen G+ servers generate different sizes of thumbnails that are used for the window specific thumbnail size, dynamic row height and the preview popup. At times your browser will load 10-20 differently sized thumbnails of an image. </span></p>
<p><span style="direction: ltr;"> I bet the feature list above is not even complete but it shows the design and engineering effort that went into this seemingly simple gallery grid. </span></p>
<h1><span style="direction: ltr;"> What if you don&#8217;t have Google&#8217;s infrastructure and manpower?</span></h1>
<p><span style="direction: ltr;">For my one man competition contribution I couldn&#8217;t possibly implement all those features. I axed the double-row-spanning title image, different thumbnail sizes and the preview popup:</span></p>
<ul>
<li><span style="direction: ltr;">Only one thumbnail size (120 pixel height, width depending on image aspect ratio)</span></li>
<li><span style="direction: ltr;">No server side cropping, only a single static thumbnail for each image.  </span></li>
</ul>
<p><span style="direction: ltr;">Still I came pretty close to the original with all the other features. </span></p>
<h1><span style="direction: ltr;"> How does the cropping in the Browser work?</span></h1>
<p><span style="direction: ltr;">To better understand how the G+ image grid worked I studied the HTML source, which basically looks like this:<a href="http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/new-3-notepad_2011-10-07_20-49-32/" rel="attachment wp-att-213"><img class="alignnone size-full wp-image-213" title="Google Plus HTML snippet explained" src="http://www.techbits.de/wp-content/uploads/2011/10/new-3-Notepad++_2011-10-07_20-49-32.png" alt="" width="992" height="357" /></a></span></p>
<p><span style="direction: ltr;">The main insight I took from this is they way the images can be cropped: using a DIV with overflow:hidden and changing margin-left of the image to define the x-offset.</span></p>
<h1><span style="direction: ltr;">How to align the images?</span></h1>
<p><span style="direction: ltr;">Once I started experimenting I realized that the image alignment is not that hard to do. Let&#8217;s say we have three images and are using a fixed thumbnail height of 120 pixel. </span></p>
<pre>image 1: 200x120
image 2: 180x120
image 3: 120x120</pre>
<p><span style="direction: ltr;">Let&#8217;s also assume we want to fit these images in a row with a given width of 460 pixels. </span></p>
<p><span style="direction: ltr;">I started to build rows of images by adding image by image until the window width is met or surpassed. </span></p>
<pre>1 image: 200 OK (&lt;460)
2 images: 200 + 180 = 380 OK (&lt;460)
3 images: 380 + 120 = 500 surpassed (break &gt;460, 40 pixels too long)</pre>
<p><span style="direction: ltr;"> So putting all three images in a row would make it 40 pixel too long. The only thing that needs to be done now is to crop all the images by overall 40 pixels to perfectly align them with the window width. </span></p>
<p><span style="direction: ltr;">My naive approach was to simply devide the excess pixels, so 40 pixel / 3 images = 13 pixels/image You have to be careful with the integer division though. To perfectly distribute the 40 pixels you need to crop one additional pixel from one of the images:</span></p>
<pre>cropped image 1: 200 - 14 = 186x120
cropped image 2: 180 - 13 = 167x120
cropped image 3: 120 - 13 = 107x120</pre>
<p><span style="direction: ltr;"> If you sum up the witdh now you get exactly 460 pixel. Combining these values with the cropping method from the previous section we get the following HTML code for the three images. </span></p>
<pre>&lt;div style="overflow:hidden; width:186px; height:120px;"&gt;
  &lt;img src="image1.jpg" style="width:200px; height:120px";
      margin-left:-7px"/&gt;
&lt;/div&gt;
&lt;div style="overflow:hidden; width:167px; height:120px;"&gt;
  &lt;img src="image2.jpg" style="width:180px; height:120px";
      margin-left:-6px"/&gt;
&lt;/div&gt;
&lt;div style="overflow:hidden; width:107px; height:120px;"&gt;
  &lt;img src="image3.jpg" style="width:120px; height:120px";
      margin-left:-6px"/&gt;
&lt;/div&gt;</pre>
<p><span style="direction: ltr;">As you can see the negative margin-left is used to center the cropped area horizontally. On thing I left out here is the spacing between images. We have 6 pixels of spacing between each image that have to be added in the inital sum. </span></p>
<p><span style="direction: ltr;">This naive approch worked pretty well even with only a single thumbnail size (120 pixels height). In a later iteration I changed the distribution of the crop-pixels though, because when equally distributing the excess pixels, portrait images tend to get too small in width. That&#8217;s why I based the crop amount on the image width, with the result that wider images are cropped more than narrow ones. </span></p>
<h1><span style="direction: ltr;">The server side </span></h1>
<p><span style="direction: ltr;">As you see above the image layout algorithm needs the widths of the individual images to do it&#8217;s mojo. In my Alfresco extension I leveraged the metadata extraction capabilties to get to the thumbnail resolution. The resulting JSON data that is finally generated on the server side is a simple array with all the image data (here is a single image as an example):</span></p>
<pre>{ "thumbs" : [
  {
    "thumbUrl" : "api/node/workspace/SpacesStore/c2f5e06f-ee15-4b0f-9e6c-1f734b4db45f/content/thumbnails/galpThumb120",
    "title" : "2010-05-02, 2 images, IMG_2173 - IMG_2174 - 5096x2320 - SCUL-Smartblend.jpg",
    "twidth" : 300,
    "theight" : 120,
    "description" : "",
    "author" : "Administrator",
    "nodeRef" : "workspace://SpacesStore/c2f5e06f-ee15-4b0f-9e6c-1f734b4db45f",
    "name" : "2010-05-02, 2 images, IMG_2173 - IMG_2174 - 5096x2320 - SCUL-Smartblend.jpg"
  }
]}</pre>
<p>The most important information here are the <strong>thumbnail dimensions</strong> and the <strong>thumbUrl</strong> that are used for the gallery grid. If you are interested in the details of the server side code in my Gallery+ extension you can <a href="http://code.google.com/p/fme-alfresco-extensions/source/browse/trunk/Gallery%20Plus%20Dashlet/config/alfresco/templates/webscripts/de/fme/dashlets/gallery.get.js">have a look in the webscript</a>.</p>
<h1><span style="direction: ltr;">Standalone JQuery demo version</span></h1>
<p><span style="direction: ltr;">To demonstrate the grid rendering independently of the full the Alfresco extension I moved the grid generation code to a standalone website with the Javascript logic based on jQuery (the original extension is based on YUI). You can see the example in action at the following URL: <a href="http://fmaul.de/gallery-grid-example/">http://fmaul.de/gallery-grid-example/</a></span></p>
<p><a href="http://fmaul.de/gallery-grid-example/"><img class="aligncenter size-full wp-image-332" title="Gallery Example Screenshot" src="http://www.techbits.de/wp-content/uploads/2011/10/gallery-example.jpg" alt="" width="520" height="285" /></a>If you try it out, make sure to resize your browser to see the dynamic resizing in action. This is not a full jQuery gallery component, sorry &#8211; it is just meant to demonstrate the rendering but you are welcome to further extend the code to a full blown jQuery gallery.</p>
<p>Note that the example gallery is <strong>not</strong> backed by a REST or thumbnail server, it uses <a href="http://fmaul.de/gallery-grid-example/images.json">this static JSON file as image source</a> for the purpose of demonstration. The page is built using <a href="http://html5boilerplate.com/">html5 boilerplate</a> and therefore my complete implementation can be found<a href="http://fmaul.de/gallery-grid-example/js/script.js"> in the scripts.js file</a>.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2011/10/25/building-a-google-plus-inspired-image-gallery/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Using the Javascript Console: Creating and populating datalists</title>
		<link>http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/</link>
		<comments>http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/#comments</comments>
		<pubDate>Tue, 18 Oct 2011 11:09:16 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[alfresco]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JavascriptConsole]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=240</guid>
		<description><![CDATA[This is the first post in a series of posts to showcase the abilities of the Alfresco Javascript Console. The Javascript Console is an Alfresco admin console extension to help developers and administrators to easily develop and execute Javascript against &#8230; <a href="http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h1><span class="Apple-style-span" style="color: #333333; font-weight: 300;">This is the first post in a series of posts to showcase the abilities of the <a href="http://code.google.com/p/share-extras/wiki/JavascriptConsole">Alfresco Javascript Console</a>. The Javascript Console is an Alfresco admin console extension to help developers and administrators to easily develop and execute Javascript against the Alfresco repository. For more information on the Javascript Console and how to install it, see the <a href="http://code.google.com/p/share-extras/wiki/JavascriptConsole">share-extras wiki page</a>.</span></h1>
<p>In this first installment I’d like to show how to create datalists and datalist items from Alfresco Repository Javascript which can be useful when you want to populate a site’s datalists automatically.<span id="more-240"></span></p>
<h1>Using the space context to select the site</h1>
<p>The first thing you should do in the Javascript Console is to select the space/folder the script is executed in. This automatically puts the node you have selected in a variable called “<strong>space</strong>”. In this example script we are selecting the Share site where the datalist shall be created. In my local installation I chose the site “jstest” as you can see in the screenshot.<a href="http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/admin-console-google-chrome_2011-09-11_22-30-00/" rel="attachment wp-att-251"><img class="aligncenter size-full wp-image-251" title="Choosing the space context in the Javascript Console" src="http://www.techbits.de/wp-content/uploads/2011/10/Admin-Console-Google-Chrome_2011-09-11_22-30-00.png" alt="" width="929" height="604" /></a>To make it even clearer which space my script is expecting I start by assigning it to a variable called “site”:</p>
<pre>var site = space;</pre>
<p>Now we have a variable site available pointing to the site where the datalist shall be created.</p>
<h2>Preparing the site</h2>
<p>The “datalists” container within a site may not exist. We need to make sure that it is available or create it if it is missing. Share usually creates this folder automatically when you create your first datalist but if you don’t have a list yet, it might be missing.</p>
<pre>var dataLists = site.childByNamePath("dataLists");

if (!dataLists) {
  var dataLists = site.createNode("dataLists", "cm:folder");

  var dataListProps = new Array(1);
  dataListProps["st:componentId"] = "dataLists";
  dataLists.addAspect("st:siteContainer", dataListProps);
  dataLists.save();

  logger.log("Created new datalists folder.");'
}</pre>
<p style="text-align: center;"><a href="http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/admin-console-google-chrome_2011-09-11_22-30-32/" rel="attachment wp-att-253"><img class="aligncenter" title="Script to create datalist items in Javascript Console" src="http://www.techbits.de/wp-content/uploads/2011/10/Admin-Console-Google-Chrome_2011-09-11_22-30-32.png" alt="" width="476" height="180" /></a></p>
<h2>Creating the datalist</h2>
<p>Now let’s create a datalist. I chose the existing contacts datalist type for this example. You can have a look in the <a href="http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/root/projects/repository/config/alfresco/model/datalistModel.xml">datalists model </a>to see which types are available and which properties can be set on each type.  Usually datalists are created with cryptic UUID names (i.e. they don’t have names defined for them) but if you create them yourself you can give them any name you like so you are able to find them later on. The Share UI only displays the datalists title, the name doesn’t show up in Share. I am using “contactlist1” as the name for the new datalist:</p>
<pre>var contactList = dataLists.childByNamePath("contactlist1");

if (!contactList) {
  var contactList = dataLists.createNode("contactlist1","dl:dataList");

  // tells Share which type of items to create
  contactList.properties["dl:dataListItemType"] = "dl:contact";
  contactList.save();

  var contactListProps = [];
  contactListProps["cm:title"] = "My Contacts";
  contactListProps["cm:description"] = "A contact list generated by a javascript.";
  contactList.addAspect("cm:titled", contactListProps);

  logger.log("Created contact datalist.");
}</pre>
<h1>Adding items</h1>
<p><span class="Apple-style-span" style="color: #000000;">Now that we have created a datalist we are ready to place items in the datalist. Since we created a contacts list we need to place items of the type dl:contact in it. We basically create a new node, set the properties of the contact node and save it, that’s all.</span></p>
<pre>var contact = contactList.createNode(null, "dl:contact")
contact.properties["dl:contactFirstName"] = "Florian";
contact.properties["dl:contactLastName"] = "Maul";
contact.properties["dl:contactEmail"] = "info@fme.de";
contact.properties["dl:contactCompany"] = "fme AG";
contact.properties["dl:contactJobTitle"] = "Senior Consultant";
contact.properties["dl:contactPhoneMobile"] = "not available";
contact.properties["dl:contactPhoneOffice"] = "not available";
contact.properties["dl:contactNotes"] = "Alfresco Expert";
contact.save();
logger.log("Created new contact: " + contact.nodeRef);</pre>
<p>When you run the complete script you see that the contact list and one contact items are created. Naturally you can create multiple contact items of you like.<a href="http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/admin-console-google-chrome_2011-09-11_22-31-08/" rel="attachment wp-att-254"><img class="aligncenter size-full wp-image-254" title="Running the script to create datalist items" src="http://www.techbits.de/wp-content/uploads/2011/10/Admin-Console-Google-Chrome_2011-09-11_22-31-08.png" alt="" width="748" height="276" /></a></p>
<p style="text-align: center;"><a href="http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/data-lists-google-chrome_2011-09-11_22-31-42/" rel="attachment wp-att-252"><img class="aligncenter size-full wp-image-252" title="An entry in the datalist has been created" src="http://www.techbits.de/wp-content/uploads/2011/10/Data-Lists-Google-Chrome_2011-09-11_22-31-42.png" alt="" width="972" height="412" /></a></p>
<p>One use case for this technique could be to create a lot of items for performance testing during development or you could generate a Javascript file based on this template to migrate content to the datalists.</p>
<h1>Wrapping up</h1>
<p>Of course this script doesn&#8217;t need to run in the Javascript Console. You could also execute it using the <a href="http://code.google.com/p/share-extras/wiki/ExecuteScriptAction">Execte Script Action</a>, my <a href="http://code.google.com/p/fme-alfresco-extensions/wiki/ScriptBootstrap">Scripts Bootstrap extension</a> or a custom webscript. You just have to make sure you have the node of the site to start with.</p>
<p>Note that the logger.log() messages are printed to the output area. In the Javascript Console these are always displayed regardless of the log level of the ScriptLogger which normally controls the logger output to the Alfresco log file.</p>
<div>
<p>I have several other javascript snippets to share with you, so stay tuned for more posts from me on “Using the Javascript Console”. Feedback, questions or ideas for other Alfresco scripting use cases are always welcome.</p>
<p>&nbsp;</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2011/10/18/using-the-javascript-console-creating-and-populating-datalists/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Adding a like button to your Alfresco extensions</title>
		<link>http://www.techbits.de/2011/10/14/adding-a-like-button-to-your-alfresco-extensions/</link>
		<comments>http://www.techbits.de/2011/10/14/adding-a-like-button-to-your-alfresco-extensions/#comments</comments>
		<pubDate>Fri, 14 Oct 2011 09:30:05 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[alfresco]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[alfresco4]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[social]]></category>
		<category><![CDATA[webscript]]></category>
		<category><![CDATA[yui]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=267</guid>
		<description><![CDATA[Alfresco 4.0 introduces social features into the Share client &#8211; the most prominent being the Like button. I&#8217;d like to give you a short tutorial on how to add the new Like button to your own Alfresco 4.0 Share extensions. &#8230; <a href="http://www.techbits.de/2011/10/14/adding-a-like-button-to-your-alfresco-extensions/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Alfresco 4.0 introduces social features into the Share client &#8211; the most prominent being the Like button. I&#8217;d like to give you a short tutorial on how to add the new Like button to your own Alfresco 4.0 Share extensions.<a href="http://www.techbits.de/2011/10/14/adding-a-like-button-to-your-alfresco-extensions/like1/" rel="attachment wp-att-278"><img class="aligncenter size-full wp-image-278" title="Like Button in Alfresco 4.0 Share" src="http://www.techbits.de/wp-content/uploads/2011/10/like1.png" alt="" width="454" height="62" /></a></p>
<p><span id="more-267"></span></p>
<h1>Webscripts Backend</h1>
<p>Let&#8217;s start with the backend. I am assuming you already have a repository webscript set up that is returning a list of nodes or a single node to the Share client as JSON objects. Now you&#8217;d like to add a like button to your client for each document.</p>
<p>In the repository javascript tier you have the ratingService at your disposal. The wiki documentation doesn&#8217;t include this service yet, so I did some digging. This is how you would determine if the current user has liked the document and the number of total likes the document has:</p>
<pre>isLiked = ratingService.getRating(node, "likesRatingScheme") !== -1;
totalLikes = ratingService.getRatingsCount(node, "likesRatingScheme");</pre>
<p>To make it even easier you can import a utility function from the document library that does this</p>
<pre>&lt;import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/parse-args.lib.js"&gt;</pre>
<p>and then use the <strong>Common.getLikes()</strong> function to retrieve the information:</p>
<pre>nodes.push({
  "name" : n.name,
  "title" : n.properties["cm:title"],
  "description" : n.properties["cm:description"],
  "nodeRef" : "" + n.nodeRef,
  "likes" : Common.getLikes(n)
});</pre>
<p>With this small change (and possibly a change to the webscript&#8217;s freemarker template) you have a JSON result from your webscript that returns the like information and looks similar to this:<span style="direction: ltr;"> </span></p>
<pre>{
  "name" : "document.txt",
  "title" : "A test document",
  "description" : "",
  "nodeRef : "workspace://SpacesStore/...",
  "likes" : {
    "isLiked": true,
    "totalLikes": 5
  }
}</pre>
<h1>Share frontend</h1>
<p>Now that we have our webscript returning the like information we need to add the like button to the Share frontend code. Here I assume you have some kind of YUI code already in place which we can extend. The share.js (webapps/share/js/share.js) utility script contains a Like Button component that we can easily use.</p>
<p>First we need to add an empty DIV to our components freemarker template.</p>
<pre>&lt;div id="${el}-like"&gt;&lt;/div&gt;</pre>
<p>Then we can add the Like component in the client javascript:</p>
<pre>new Alfresco.Like(this.id + '-like').setOptions({
     siteId: this.options.siteId, 
     nodeRef: item.nodeRef,
     displayName: item.name
}).display(item.likes.isLiked, item.likes.totalLikes);</pre>
<p>This is all you need to display the Like button.</p>
<ul>
<li><span style="direction: ltr;">You only have to set the initial values using the display() function. When the user clicks the Like button the totalLikes value is automatically increased by one.</span></li>
<li><span style="direction: ltr;">This works for documents (for folders you need to set the <strong>type</strong>=&#8221;folder&#8221; option).</span></li>
<li><span style="direction: ltr;">You could leave out the <strong>siteId</strong> if your component is not site specific. It is used to post the like event to the site&#8217;s activity stream.</span></li>
</ul>
<p>The only problem I had with this component is that it does not provide a callback or an event when the like status is updated. It posts the like status the the repository, changes it&#8217;s display state but since it doesn&#8217;t provide feedback you can not update the state of your data model (the item variable) accordingly. This means whenever you redraw or create the Alfresco.Like component you have to make sure you read the most current state of the node from the repository webscript.</p>
<p>All in all adding the like button is pretty painless and I hope you find it as easy to add to your Alfresco extensions.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2011/10/14/adding-a-like-button-to-your-alfresco-extensions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Recursive file iteration in Java</title>
		<link>http://www.techbits.de/2010/02/11/recursive-file-iteration-in-java/</link>
		<comments>http://www.techbits.de/2010/02/11/recursive-file-iteration-in-java/#comments</comments>
		<pubDate>Thu, 11 Feb 2010 21:15:48 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[apidesign]]></category>
		<category><![CDATA[bestpractice]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=198</guid>
		<description><![CDATA[In a java frameworks talk I gave recently I showed the following example for finding files recursively&#8230; public void listFilesInDirectory(File dir) { File[] files = dir.listFiles(); if (files != null) { for (File f : files) { if (f.isDirectory()) { &#8230; <a href="http://www.techbits.de/2010/02/11/recursive-file-iteration-in-java/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In a java frameworks talk I gave recently I showed the following example for finding files recursively&#8230;</p>
<blockquote>
<pre>public void listFilesInDirectory(File dir) {
  File[] files = dir.listFiles();
    if (files != null) {
      for (File f : files) {
         if (f.isDirectory()) {
	    listFilesInDirectory(f);
	 }
	 else {
	    System.out.println(f.getName());
	}
     }
  }
}</pre>
</blockquote>
<p>&#8230; which in real projects often grows to a larger block of code. The web is full of code blocks like this for walking a directory tree. The best version I came across is<a id="wylk" title="this object oriented one by Torsten  Curdt" href="http://vafer.org/blog/20071112204524"> this object oriented one by Torsten Curdt</a>. Since you usually don&#8217;t want to write this yourself, I suggested in my talk to use FileUtils which makes recursive iteration much easier:</p>
<blockquote>
<pre>Collection jspFiles = FileUtils.listFiles(rootDirName,
                        new String[] { "jsp" }, true);</pre>
</blockquote>
<p>This looks concise and useful but as I tried to use it, I wasn&#8217;t too pleased with the FileUtils&#8217; solution. Here is why:</p>
<ul>
<li>The recursion is processed in one go, i.e. all results are written to a List even when using the iterateFiles method. The recursion is not processed iteratively.</li>
<li>You can not influence the directories that are searched.</li>
<li>Only files are returned, you can not search for directories.</li>
<li>The API is not very expressive (e.g. what does the &#8220;true&#8221; mean).</li>
<li>No generics (raw collection types are returned).</li>
</ul>
<h1>A Better API</h1>
<p>Not being satisfied with the solutions I found, I &#8220;dreamed up&#8221; my own API for listing and finding files. I don&#8217;t consider it complete but for the most part I am pleased with the ease of use that the builder pattern provides. The code for this can currently be found in an <a id="l6db" title="unrelated goole code project" href="http://code.google.com/p/data-integrity-check/source/browse/#svn/trunk/src/main/java/de/fmaul/common/io">unrelated goole code project</a>. The rest of this article shows the functions that are currently supported.</p>
<h2>Find files two ways</h2>
<p>There are generally two ways to use the result &#8211; as interator or as list:</p>
<p><span style="font-family: Arial;">1. Iterate over all files in the windows directory:</span></p>
<blockquote>
<pre><span style="font-family: Courier New;">for (File f : <strong>Files.find</strong>("c:\\windows")) {</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">}</span></pre>
</blockquote>
<p>2. Get all the files in a directory as a list of files:</p>
<blockquote>
<pre><span style="font-family: Courier New;">List&lt;File&gt; allFiles = <strong>Files.find</strong>(somedir).<strong>list()</strong>;</span></pre>
</blockquote>
<p><span style="font-family: Verdana;">Except from the return type the second version does the same </span><span style="font-family: Courier New;"><span style="font-family: Verdana;">as the JDK command listFiles:</span></span></p>
<blockquote>
<pre>File[]  allFiles = (new File(somedir)).listFiles()</pre>
</blockquote>
<h2>Easy recursive listing</h2>
<p>To iterate all the files in the C:\Windows directory, you would use:</p>
<blockquote>
<pre><span style="font-family: Courier New;">for (File f : Files.find("c:\\windows").<strong>recursive()</strong>) {</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">}</span></pre>
</blockquote>
<p>Note: This actually works iteratively, i.e. the recursion happens as you fetch files from the iterator. The result is not fetched into a huge list.</p>
<p>With a Predicate you can limit the recursion to specific directories. In this example all .svn directories within a source tree are skipped:</p>
<blockquote>
<pre><span style="font-family: Courier New;">Predicate&lt;File&gt; noSvnDirs = new Predicate&lt;File&gt;() {</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;"> boolean apply(File file) {</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;"> return !file.getName().equals(".svn");</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">}</span></pre>
<pre><span style="font-family: Courier New;">for (File f : Files.find("src/java/").<strong>recursive(noSvnDir)</strong>) {</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">}</span></pre>
</blockquote>
<h2>Want Files, Directories or both?</h2>
<p>Define if you want only files, only directories or both in your result with yield*()-Methods.</p>
<blockquote>
<pre>Files.find(someBaseDir).recursive().yieldFiles()  // this is the default
Files.find(someBaseDir).recursive().yieldDirectories()
Files.find(someBaseDir).recursive().yieldFilesAndDirectories()</pre>
</blockquote>
<h2>Filtering the results</h2>
<p>To get all textfiles within a dir use:</p>
<blockquote>
<pre>Files.find(dir).withExtension("txt").list();
Files.find(dir).ignoreCase().withExtension("txt").list();</pre>
</blockquote>
<p>You can also filter by Name, e.g. to find README files:</p>
<blockquote>
<pre>Files.find(dir).withName("README").list();
Files.find(dir).ignoreCase().withName("readme").list();</pre>
</blockquote>
<p>Note that the default matching is case sensitive. The commands caseSensitive() and ignoreCase() can be used to toggle the matching behaviour.</p>
<p>For special needs you can also specify a Predicate&lt;File&gt; to filter the resulting files.</p>
<blockquote>
<pre>Files.find(dir).recursive().withFilter(somePredicate).list();</pre>
</blockquote>
<h2>Finding Directories</h2>
<p>When looking for directories there are some special usecases that are supported, e.g. looking for directories that contain a specific file:</p>
<blockquote>
<pre>Files.find(dir).recursive().yieldDirectories()
               .containingFile("Thumbs.db");</pre>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2010/02/11/recursive-file-iteration-in-java/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Android exploration continued</title>
		<link>http://www.techbits.de/2010/01/04/android-exploration-continued/</link>
		<comments>http://www.techbits.de/2010/01/04/android-exploration-continued/#comments</comments>
		<pubDate>Mon, 04 Jan 2010 12:01:13 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=192</guid>
		<description><![CDATA[I extended my android application with a preferences screen now, which is quite easy to do. A tutorial for creating a Preferences Activity got me started &#8211; unfortunaltely the xml preferences definition it uses is incorrect. The tags are names &#8230; <a href="http://www.techbits.de/2010/01/04/android-exploration-continued/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I extended my android application with a preferences screen now, which is quite easy to do.</p>
<ul>
<li>A <a href="http://www.androidguys.com/2008/09/29/whats-your-preference-part-one/">tutorial for creating a Preferences Activity</a> got me started &#8211; unfortunaltely the xml preferences definition it uses is incorrect. The tags are names of Classes which have to be capitalized.</li>
<li>The open the preferences activity I added a <a href="http://developer.android.com/guide/topics/ui/menus.html">Option Menu</a>.</li>
<li>I wanted to add some kind of progress indicator. I ended up using the <a href="http://developer.android.com/reference/android/app/ProgressDialog.html">ProgressDialog </a>and the <a href="http://developer.android.com/reference/android/os/AsyncTask.html">AsyncTask </a>to run the downloading and xml parsing in the background. To fix issues with device rotation I might have a look at the BetterAsyncTask in the <a href="http://github.com/kaeppler/droid-fu">Droid-FU library</a> later.</li>
<li>I also ran into DateFormat and Date issues with the UTC formated Date in the XML file. I Thought about using <a href="http://joda-time.sourceforge.net/"> joda-time</a> at least twice but then stuck to the JDK implementation for smaller app size. The fact that android brings it&#8217;s own class named DateFormat which just provides a localized JDK-DateFormat object doesn&#8217;t help either.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2010/01/04/android-exploration-continued/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>First Steps with Android</title>
		<link>http://www.techbits.de/2009/12/30/first-steps-with-android/</link>
		<comments>http://www.techbits.de/2009/12/30/first-steps-with-android/#comments</comments>
		<pubDate>Wed, 30 Dec 2009 10:02:54 +0000</pubDate>
		<dc:creator>Florian</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[mobile]]></category>

		<guid isPermaLink="false">http://www.techbits.de/?p=189</guid>
		<description><![CDATA[In the last few days I started out with some android development. Here are some things learned so far developing my first app: The Android Tutorials are a great starting point, though i only followed through with the HelloWorld Tutorial. &#8230; <a href="http://www.techbits.de/2009/12/30/first-steps-with-android/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In the last few days I started out with some android development. Here are some things learned so far developing my first app:</p>
<ul>
<li>The Android Tutorials are a great starting point, though i only followed through with the <a href="http://developer.android.com/intl/de/resources/tutorials/hello-world.html">HelloWorld</a> Tutorial. In retrospect I should have looked at the <a href="http://developer.android.com/intl/de/resources/tutorials/notepad/index.html">Notepad Tutorial</a> a little closer because it explains important concepts (namely activities/intents).</li>
<li><a href="http://www.anddev.org/">http://www.anddev.org/</a> is a useful source for tutorials and code snippets</li>
<li>It&#8217;s still java but a completely different API, so you often have to look for classes and methods via code completion oder in examples to get things done.</li>
<li><strong>Downloading:</strong> Can be done with the included HTTP Client library. Unfortuantely <a href="http://dlinsin.blogspot.com/2009/08/http-basic-authentication-with-android.html">Android still uses an old version of the HTTP Client</a> though, which made it hard to find documentation (e.g. how to set authentication credentials). Additionally you shoudn&#8217;t forget to declare the INTERNET-permissions in your application manifest.</li>
<li><strong>Storing and retrieving Files</strong> looks fairly easy (getDir(), getCacheDir()-Methods are there) at first sight but you have to unerstand the Android filesystem security model if you don&#8217;t want to spend hours with debugging. The before mentoined methods use internal storage where each application stores it&#8217;s data independently. Public read/write (e.g. file exchange with other applications) is only possible when you store your content with the specific method openFileOutput(). The external SD card on the other hand can be openly accessed with the regular Java File API.</li>
<li><strong>XML Parsing:</strong> I started out with the sax parser but since my XML file was pretty complex I ditched it and downloaded<a href="http://brainflush.wordpress.com/2009/05/19/the-force-unleashed-xmlxpath-on-android-using-dom4j-and-jaxen/"> dom4j</a> which has a really easy to use API. Unfortunately it adds at least 200KB of final app size. I now realized I could have gone with the<a href="http://www.anddev.org/parse_xml_with_dom_-_getnodevalue_always_null-t3082.html"> regular DOM parser</a> which has a decent API. I&#8217;ll have to reevaluate this later &#8211; maybe the end user responsiveness does require the faster streaming parser approach (sax).</li>
<li><strong>UI design:</strong> Declarative XML based looks powerful and well thought out but I mostly stuck to tutorial layout for now. This is an area I still have to get into.</li>
</ul>
<p>Ok, that&#8217;s it for now. Android is turning out to be a great plattform &#8211; exciting times.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.techbits.de/2009/12/30/first-steps-with-android/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

