Saturday, 24 December 2011

Unix Domain Sockets on OSX


It's been a while since I've done anything with Unix domain sockets, and it's not the same on OSX!

If we take a look at the structure for sockaddr_un we see that the standard is:
struct sockaddr_un {
  sa_family_t sun_family; /* AF_UNIX */
  char sun_path[108]; /* pathname */
};


However, on OSX it's:
struct sockaddr_un {
  unsigned char sun_len; /* length including null */
  sa_family_t sun_family; /* AF_UNIX */
  char sun_path[104]; /* pathname */
};


Next, the standard ( or at least as far as I am aware of ) way to call bind() is as follows:

len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
  Didn't work;
}

In the above, local is the sockaddr_un struct, and the path is /var/tmp/socket.

Now as we all know ;) the normal practice is to call unlink() on the path before attempting the bind just in case the program has crashed and left the socket file lying around, however under OSX with the above code, unlink is unable to find the file.

This is because the name of the socket becomes truncated by one character, to /var/tmp/socke

It took some digging, but the correct way to call bind under OSX is as follows:

if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
  perror("bind");
exit(1);


This will create the socket with the full path name, here's a complete code snippet:
#define SOCK_PATH "/var/tmp/socket"
struct sockaddr_un local;
char str[100];
int s;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
 perror("socket");
 exit(1);
}

if (strlen(SOCK_PATH) >= sizeof(local.sun_path)) {
 perror("path to long!");
 exit(1);
}

local.sun_len = sizeof(local);

local.sun_family = AF_UNIX;

strcpy(local.sun_path, SOCK_PATH);

unlink(local.sun_path);

if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
perror("bind");
exit(1);
}

//GO off an do what you want here

This caught me out with much head scratching for some time, so I hope it helps someone out there!


Sunday, 11 December 2011

Adobe XMP Panel - Improvements

After publishing the original post about creating a custom XMP panel ( see here ), I was contacted via the Resource Space forum about wether it was possible to create multiple sets of check boxes on the panel that related to different XMP fields, ie:

chk1,chk2,chk3 = XMP field 1
chk4,chk5,chk6 = XMP field 2

I posted on the forum that this would be easy to do using multiple handlers, and calling the relevant handler function when a checkbox is ticked.
Now many of you will note that this is not an elegant solution, but it would work, and was easy to explain!
I started thinking about this further, and came up with a more elegant way to achieve the task, that was also much easier to expand upon as there was only one place you needed to add extra code.
So this post will attempt to describe what I've done, and hopefully make it easier for others to reuse the code.

Step 1 - Layout the Panel
The first thing we need to do is layout the panel, as before this is done using the grid control, and using a naming convention for the id's of the checkboxes that relate to the label of the checkbox but without spaces.

<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="Road Haulage"
id="_chkRoadHaulage"    click="updateCats(event,'docTitle')"/>
</mx:GridItem>

The label "Road Haulage" above has an id of "_chkRoadHaulage" so that we can reference it in code easily, as show in the previous article.
The major change to the layout of the checkboxes is the click event. The function that is called now has a second parameter that is the id of the XMP text input that the check box should modify.

<fi:XMPTextInput id="docTitle"
xmpPath="mc:MainCategory
xmpType="Localized"
name="docTitle"
visible="false"
/>

The second set of checkboxes have a different XMP Text Input.

<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="House"
id="_chkHouse"
click="updateCats(event,'docBuilding')"/>
</mx:GridItem>

<fi:XMPTextInput id="docBuilding"
xmpPath="mc:Building
xmpType="Localized"
name="docBuilding"
visible="false"
/>

I'm also setting visible to false as I don't want to give the user the ability to manually edit the field and screw everything up ;)

Step 2 - Code Changes
Next we need to make some changes to the code.
The first thing I thought about is how we handle multiple arrays and reference them easily?
I decided the easiest way to do this was to keep the cats array and store the arrays within that refernced with a key that is the id of the XMP text input.
So the new function that gets called when the panel has completed loading is now very simple.

private function catsReadHandler(event:Event):void
{
/*  This is called once the panel has completed initialising.
        */
// create an array for each xmp field
cats['docTitle'] = new Array();
cats['docBuilding'] = new Array();
//populate the arrays and tick the right boxes
chkBoxes('docTitle');
chkBoxes('docBuilding');
}


All we have to do now is create the sub array for each XMP text input.
The next part calls a new function that reads the text input, populates the array if it contains data and then ticks the boxes that were referenced in the text input. It's basically the same as the previous catsReadHandler function, but I'm using the this[controlID] nomenclature to reference the fields and the checkboxes.
This gets called for each set of checkboxes / XMP field in the panel.

Here's the function:

private function chkBoxes(xmpFieldName: String):void
{
// this takes the name of the xmp field, populates the array, and ticks the checkboxes
var outStr:String;
var catsString: String = this[xmpFieldName].text;
var chkName:String
var catStr:String;
if (catsString.length > 0)
{
// split the text by commas and populate the array
cats[xmpFieldName] = catsString.split(",");
for (var i:int = 0; i < cats[xmpFieldName].length; ++i) 
{
// remove spaces from the cat name and add to _chk to ref the box
chkName = "_chk";
catStr = cats[xmpFieldName][i];
catStr.replace(/s+/g, "");
chkName += catStr;
try {
// tick the box!
this[chkName].selected=true;
} catch (someError:Error)
{
this[xmpFieldName].text=i + " : " + " " + chkName + " " + someError.message;
}
}
}
}

As you can see, it's pretty much the same as before, but putting this in a separate function enables us to simplify the catsReadHandler function and only have to add extra code there.

The final piece of the puzzle is the function that gets called when a user checks / un-checks a box.
This now takes the id of the XMP text input to update, and so we can reference that directly without having to write a special function for each set of check boxes & XMP fields.

private function updateCats(event: Event,fieldName: String):void
{
//we use event.target to identify the control that triggered the function :)
var catName:String = event.target.label;
var outStr:String;
var cName:String;
var delItem:Boolean;
var localArray:Array;
// copy the main array so we can manipulate it.
localArray = cats[fieldName];
if (localArray.length == 0)
{
localArray.push(catName);
} else {
for (var i:int = 0; i < localArray.length; ++i) {
if (localArray[i] == catName)
{
localArray.splice(i,1);
delItem = true;
}
}
if (!delItem)
{
localArray.push(catName);
}
}
cats[fieldName]=localArray;
outStr = cats[fieldName].toString();
this[fieldName].text = outStr;
this[fieldName].dispatchEvent(new Event(Event.CHANGE));
} 

One of the major changes here apart from calling the function with the field id is that I'm copying the array before manipulating it and then copying it back to the main array after the manipulation is done.
I know in theory I shouldn't need to do this, but I was having issues manipulating the main array directly, so I chose this method because I couldn't be bothered to debug it any further than I already had ;)

The complete file can be found here, feel free to play around with it.




Thursday, 8 December 2011

Installing Resource Space on OSX Lion Server

I've posted on our company blog about installing Resource Space on a Lion Server.

You can read it here

Wednesday, 30 November 2011

ResourceSpace and Static Sync

Some of our clients have a workflow which uses static sync to add the assets to ResourceSpace, but does not ingest them.

The assets are kept on a read only file share, and the users link to the assets within InDesign etc.
One of the problems encountered with this kind of workflow is how do the users find an asset on the file share from within RS?

To overcome this problem, I've written a simple plugin that will open a finder window of the folder that contains the asset.



The only thing you have to configure is the afp server path to the root of the share. This might work for smb as well, but I haven't tested that yet!




To install it, unzip the file and place in the plugins folder.

You can get the file here

AD / OD Plugin - Some tweaks!

I've had feedback from people who have been testing and using this plugin, and there a number of changes that have been made which I though it would be best to document here.

stristr()
If you are a running an older version of PHP ( pre 5.3 I believe ) and you get the following error:
Wrong parameter count for stristr()

Change line 101 in ldap_class.php from:
$usercn = stristr($username,"@",true);
to:
if($pos=stripos($username,"@")) $usercn=substr($username,0,$pos);

This is because the function stristr was changed in version 5.3 and I'm using the newer features.

Changing the name displayed in RS
By default, the plugin is set to display the users name as FirstName LastName in RS, but this can be changed.
To use the displayname container in AD for example, change the following in ldap_class.php

Line 142, change from:
$retArr = array("sn", "givenname", "mail","cn");
to:
$retArr = array("sn", "givenname", "mail","displayname");

Line 152, from:
if (isset($info[0]['cn'][0])) { $retVar['cn'] = $info[0]['cn'][0]; }
to:
if (isset($info[0]['displayname'][0])) { $retVar['cn'] = $info[0]['displayname'][0]; }

This only works for AD, for OD other changes would have to be made. I intend to make this a drop down selection in the next version of the plugin.

Domain Login
When using Active Directory, the user is required to login with the format user@domain.
The reason for this is that the plugin is designed to work in an environment with cross domain trusts.
If you don't have multiple domains, then it's possible to change this behaviour by hardcoding the domain name into ldap_class.php

Change line 89 in ldap_class.php from:
$this->ldaprdn = $username;// ."@".$userContainer;
to:
$this->ldaprdn = $username ."@". "domainName";

Change line 103 from:
$filter="(samaccountname=".$usercn.")";
to
$filter="(samaccountname=".$username.")";

Again, this is a config feature I'm going to add to the next version.

I'd like to say thanks to everyone that has helped test and improve this, I'll add more tweaks as they become known.  

Monday, 28 November 2011

Irving Thalberg

Someone we could all learn from!

http://en.wikipedia.org/wiki/Irving_Thalberg

UK Government backs call for classroom coding

There's an interesting debate going on at the moment about teaching coding in schools.

See:
http://www.bbc.co.uk/news/technology-15923113

What I find more interesting is what I perceive as the situation outside of the school room.

When I was at school, we had computers that didn't do much, unless you programmed them. I was fortunate enough that my parents bought me a computer, which I also had to program to do things, and so subsequently I learnt to program.

Today, computers at home are generally used for surfing the internet, emails etc, and very rarely used for programming.

The necessity is no longer there, and the complexities of learning to program a windows or mac computer when compared to the simplicity of something like a ZX81 are huge.

When I was young, getting a computer to say "I love you" to my girlfriend at the time in a for next loop was exciting, but to todays social media aware teenager I can image that the expression would be "whatever, have you seen the new version of Halo?".

The need to learn to program is no longer there. Where's the primitive excitement in this world of the gooey and the games?

The article complains about over emphasis on how to use software rather than how to write it.

What they don't seem to take into account is that in order to write code one has to be a hacker, a geek, interested in the fundamental design concepts and engineering challenges that have traditionally been associated with traditional engineering disciplines.

It's not without reason that we have titles like "Software Engineer" and "Software Architect. These titles are representative of the other disciplines that they reference, such as "Electrical Engineer" and "Architect".

Not everyone is cut out to be an engineer, just as not everyone is cut out to be an atomic researcher.

When I was younger computer science, programming and engineering was taught at university, and surprise surprise, it still is.

The kids today need to know how to use a computer because they are liable to experience them in most jobs. Art students need a foundation in digital photography and Photoshop, In Design, Illustrator, because they need to know not only how to draw but how to work in the digital world that any job in that field will require knowledge of. Someone working in Maccy D's needs to know how to use the till.

I learnt technical drawing amongst other things, which has now been replaced by Google Sketchup and Autocad, yet the foundations of that discipline enable me to understand how to use the software ( and my geekiness of curiosity ;) 0

So what am I saying? We don't and shouldn't need to teach kids to write software in general school, but we should teach curiosity. My partner is a school teacher and it amazes me the crap they have to teach and the way it has to be taught.

I say, teach the basics, but above all teach curiosity and the ability to learn, and then let the kids find out what it is they love and want to excel in, and then encourage them. There is nothing better for a human being than to love what they do and feel good about the impact they have on those around them, and especially society in general if they are lucky enough to have that impact.

I don't understand why think tanks who have no experience in teaching, or even understanding beyond a closed view of the world, should have the right to decide that teaching this weeks fashionable ideology is more important than exciting the kids to be curios and encouraging them to explore that curiosity?

I feel that what we need to do is encourage kids to have the right to be individual, and celebrate that, rather than try and get conformity to a uniform baseline for stats and school league tables.

Some kids will be curious, and become great software engineers. Some won't but will become amazing artists and challenge our perspectives in both the digital and analogue worlds. Others will become customer service experts and help to develop customer interaction portals that will be a joy to use.

Others won't be any of the above, but will provide the most essential parts of a modern society that are looked down upon. They will be the bin men, the recycling agents, the street cleaners. These people are essential to our society, and should be paid as much if not more than the bankers who keep ruining the economy for everyone else! ( oooh, a bit of controversy there  ;) )
Give your bin men, post women* etc a cash gift at Christmas. That is the real tradition of boxing day, to give a gift to those who serve you. I'm not saying that they are servants to us, but they are civil servants, servants to society who keep it working for the rest of us.
If you see them in the morning, say thank you, because without these people modern society would crumble.

So, my new algorithm is:

var kids = new curiosity();
kids.explore();
kids.develop();

set kids.future = bright;




* I'm not being sexist, just drawing on traditional writing... maybe I should have said person?