Saturday, 9 March 2019

Gmail account archiving - why is it so hard?

Long time no post, I think I'm averaging one post a year now!

Whilst we love G Suite, especially the API of which we make extensive use, I can't work out why there is no easy way to archive a users email when you delete them.

So a member of staff leaves, and as part of the delete process one can transfer all the files they own to another user, so we transfer all files to an archive account and Google kindly puts all those files in a folder named the same as the user, brilliant!

But why oh why can't we do the same thing with email?

Some of our clients have to keep all correspondence for a certain amount of time for legal reasons, however there is no easy workflow for this that I can see, and so we have a lot of dormant accounts sitting there "just in case"

OK: I know I could download all the email via IMAP using Thunderbird or similar and then either upload to a Gmail archive account or move the whole lot to Glacier via S3 in MBOX format.
But the later doesn't give me search-ability and the former is a PITA to organise.

I just don't understand why Google have made G Suite so damn easy for a sysadmin to manage and have produced a fantastic API that allows us to automate workflows efficiently, but we can't do something so seemingly simple as click and "archive email to" button?

Monday, 20 June 2016

Let's Encrypt - Updating Cert on Amazon AWS Linux AMI

So I'm using Certs from Let's Encrypt ( letsencrypt.org) for the web servers I run on EC2.
I'm also using the Amazon Linux AMI so that I have all the S3 tools etc pre installed.
Amazon's version of Python 2.7 appears to have some issues, but I managed to get round them by piecing together some guides on the web to get LE installed and working.

However, in the spirit of good practice, I ran a package update recently, which overwrote the changes I had made to get LE working, and borked the cert update process :(

The error I was getting whilst running the update command in debug was:

letsencrypt-auto: line 167: virtualenv: command not found

The suggested fix was to run pip upgrade then pip install virtualenv.

This doesn't work :( The fix was to do the following:

sudo easy_install --upgrade pip
sudo easy_install virtualenv

After this I was able to update the cert.

Posting here to remember it and in case it helps someone!

Tuesday, 1 December 2015

Extending SNMP

One of the things I love about SNMP is how easy it is to extend and use it as a wrapper service.

I was training some of my colleagues today on SNMP and as part of the course we covered extending SNMP in detail.

Beyond the simple things like echoing back a value, it's great that it can call external scripts and pass arguments to them.

An example of this is to be able to get the percentage of used disk space on a hard drive.
To extend snmp, we add a line to snmpd.conf file.
extend rootspace /bin/bash /scripts/getdiskspace.sh /dev/disk1

/scripts/getdiskspace.sh - the script to call

/dev/disk1 - the parameter to pass to the script.

Once we have set this up, we can get all of the extended parameters by walking the NET-SNMP-EXTEND-MIB::nsExtendObjects OID

snmpwalk -c public 192.168.0.2 NET-SNMP-EXTEND-MIB::nsExtendObjects

Which gives us:
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 2
NET-SNMP-EXTEND-MIB::nsExtendCommand."dataspace" = STRING: /bin/bash
NET-SNMP-EXTEND-MIB::nsExtendCommand."rootspace" = STRING: /bin/bash
NET-SNMP-EXTEND-MIB::nsExtendArgs."dataspace" = STRING: /scripts/getdiskspace.sh /Volumes/Data
NET-SNMP-EXTEND-MIB::nsExtendArgs."rootspace" = STRING: /scripts/getdiskspace.sh /dev/disk1
NET-SNMP-EXTEND-MIB::nsExtendInput."dataspace" = STRING: NET-SNMP-EXTEND-MIB::nsExtendInput."rootspace" = STRING: NET-SNMP-EXTEND-MIB::nsExtendCacheTime."dataspace" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."rootspace" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType."dataspace" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendExecType."rootspace" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."dataspace" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."rootspace" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage."dataspace" = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStorage."rootspace" = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStatus."dataspace" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendStatus."rootspace" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."dataspace" = STRING: 93
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."rootspace" = STRING: 76
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."dataspace" = STRING: 93
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."rootspace" = STRING: 76
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."dataspace" = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."rootspace" = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendResult."dataspace" = INTEGER: 0
NET-SNMP-EXTEND-MIB::nsExtendResult."rootspace" = INTEGER: 0
NET-SNMP-EXTEND-MIB::nsExtendOutLine."dataspace".1 = STRING: 93
NET-SNMP-EXTEND-MIB::nsExtendOutLine."rootspace".1 = STRING: 76

The info we are interested in are the output lines:

NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."rootspace" = STRING: 93
NET-SNMP-EXTEND-MIB::nsExtendOutLine."rootspace".1 = STRING: 93

The result value gives the exit status of the script, so we could pass an integer on exit to this parameter

NET-SNMP-EXTEND-MIB::nsExtendResult."rootspace" = INTEGER: 0

To get just a single response with snmpget, we would use the following:

snmpget -c public 192.168.0.2 'NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."rootspace"'
which responds with:

NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."rootspace" = STRING: 76

This format of the above line is:
extend - the extend directive
rootspace - the extension command that the daemon will respond to
/bin/bash - the external environment to run the extension in

Sunday, 29 November 2015

Thoughts on the future of IT

I have just become a certified AWS Solutions Architect, and along the journey to becoming qualified a new perspective regarding the future of IT for the SMB market is slowly dawning on me.

In the past the standard procedure was to put kit on premise, and then to maintain it as hosting it / paying a provider was quite often more expensive.

Now however the market is changing and it no longer makes sense to put kit on premise due to the economies of scale and high resilience offered by hosting services such as AWS.

Couple this with the decreasing cost of internet connectivity, and the move to truly cloud based infrastructure makes sense.

I am currently designing HA systems that have little to no on premise infrastructure, with almost everything provided as a managed service.

This moves most of the cost into OP-EX, and reduces the TCO drastically, and seems to be a no brainer!


Saturday, 23 August 2014

NFS Client Settings on OSX 10.9

In OSX 10.9 you can specify the options that the Finder uses to connect to NFS shares by putting them in /etc/nfs.conf

For example
#
# nfs.conf: the NFS configuration file
#
nfs.client.mount.options = nolocks,locallocks,intr,soft,nfcple:

This brings back the functionality available in 10.5 an 10.6

I have noticed that where I had to sometimes explicitly specify resvport in 10.5 and 10.6, this no longer seems to be necessary in 10.9

Thursday, 14 August 2014

Reset an OSX Server

There's an easy way to reset an OSX 10.8 and 10.9 server back to initial settings.

1. Drag the server app to the trash, the system will detect this and then warn you that it is stopping all services.
2. Rename /Library/Server to /Library/Server.old
3. Reboot the machine ( maybe not necessary, but might be needed to clear any stuck processes )
4. Move the Server app back into Applications
5. Launch it. It will start afresh.

Tuesday, 29 July 2014

Backing up and Nuke OSX 10.8 Calendar Server

Backup

There's various bits of documentation around about how to do this on 10.7 Server, but although the principal is correct it doesn't work on 10.8

The reason for this is that in 10.8 there are two instances of the postgres daemon.

One is in user land, for sysadmins to setup their own databases, and is also used by Roundcube Topic Desk. The second is hidden and is used to store the servers data such as calendar events and wikis.

The 10.7 instructions to backup the DB are:
sudo pg_dump -U _postgres caldav -c -f caldav.sql

If you run this on 10.8 server it will fail, saying that it can't find the caldav database.

The only way to access the caldav db is via a unix domain socket located at:
/Library/Server/PostgreSQL\ For\ Server\ Services/Socket/.s.PGSQL.5432

You can verify this by using telnet to connect to it:
telnet -u /Library/Server/PostgreSQL\ For\ Server\ Services/Socket/.s.PGSQL.5432

** Note, a service that uses the postgres db must be running for the socket to exist.

In addition, the pg_dump program located on the standard path, i.e. /usr/bin is the wrong version to access the postgres damon hosting the service databases, there is another version hidden away within the server app at:
/Applications/Server.app/Contents/ServerRoot/usr/bin/

Even if you call this directly, it will fail because it tries to access the standard TCP port for postgres.

So, we need to use the pg_dump in the server app, and pass the socket location to it.

Luckily we can do this with the host flag, from the man page:
-h host, --host=host
           Specifies the host name of the machine on which the server is running. If the value begins with a slash, it is used as the directory for the Unix domain socket.
           The default is taken from the PGHOST environment variable, if set, else a Unix domain socket connection is attempted.

It appends the socket file name, so actually only wants the path.

Putting this all together, we can successfully backup the caldav DB with the following command:
/Applications/Server.app/Contents/ServerRoot/usr/bin/pg_dump -U _postgres -h /Library/Server/PostgreSQL\ For\ Server\ Services/Socket/ caldav -c -f caldav.sql

This will backup the DB that contains all the events, however there is also another program hidden in the server app that we need to run that will backup the sqlite dbs and the server settings.

/Applications/Server.app/Contents/ServerRoot/usr/sbin/calendarserver_backup

To backup, pass the following:
/Applications/Server.app/Contents/ServerRoot/usr/sbin/calendarserver_backup backup file.tgz

To restore
/Applications/Server.app/Contents/ServerRoot/usr/sbin/calendarserver_backup restore file.tgz

If you run it without any options or with -h it will give you basic help info.

Nuke and Rebuild

So now we know how to backup the DB, what if we want to wipe it and start again?
To do this we need to drop the caldav db, and then run calendarserver_bootstrap_database to recreate the DB.

However, the socket only exists when services that use the db are running, and you can't drop the db if it's being used.

To overcome this, luckily the wiki service uses the postgres daemon and creates the socket, but does not lock the caldav db.

Step 1…. get the background postgres daemon working..

In Server App
Stop Calendar
Stop Contacts 
Start Wiki

Step 2.. drop the caldav DB

sudo /Applications/Server.app/Contents/ServerRoot/usr/bin/dropdb -U _postgres -h /Library/Server/PostgreSQL\ For\ Server\ Services/Socket/ caldav

Step 3 … rebuild the caldav DB

sudo calendarserver_bootstrap_database -v


Tuesday, 17 June 2014

Ubuntu Mount HFS +

HFS Volumes can be mounted on a linux system, here's the instructions for ubuntu (12.04 LTS):
First, make sure that you have hfsprogs installed. Example installation command:
sudo apt-get install hfsprogs 
Next, mount or remount the HFS+ drive; commands need to be as follows:
sudo mount -t hfsplus -o force,rw /dev/sdx# /media/mntpoint 
or
sudo mount -t hfsplus -o remount,force,rw /mount/point 
Finally, if the drive was improperly unmounted or has otherwise become partially corrupted run fsck.hfsplus (provided here by Jayson) as such:
sudo fsck.hfsplus /dev/sdx#

Mounting an OSX SMB Share on Linux

I got bit by this recently, and so sticking it here for when I forget next time ;)

Depending on the version of OSX you might have to specify different security settings:
mount.cifs //server_ip/share_name /mount_point -o username=osx_user,noexec,nounix,sec=ntlm,no perm,rw
This generally works, but you might need to change sec to sec=ntlmssp
example
First create the mount point if it doesn't exist:
sudo mkdir /mnt/Public
Then mount it:
sudo mount.cifs //192.168.1.1/Public /mnt/Public -o username=admin,noexec,nounix,sec=ntlm,no perm,rw
To unmount it:
sudo unmount /mnt/Public

SMTP Authentication with Archiware PresStore

As there are issues with sending reports from PresStore to mail servers  that require smtp authentication, I've created a script based on another script by  that will do this.
You will need php and pear installed on the server, but obviously this means it will work on Windows, linux and OSX.

Check to see if you have pear installed

$ pear version
If you don’t have it installed, you will need to install pear,
OSX 10.9 ( might work for other versions as well )
cd /usr/lib/php
sudo php ./install-pear-nozlib.phar
edit /etc/php.ini and add /usr/lib/php/pear to the include paths
Others
see here for info:

Check to see if the Mail Class is Installed

pear list-files Mail
If you don’t get anything then install it !
sudo pear install mail
You will also need Net_SMTP
sudo pear install Net_SMTP


Install the Scripts

Download the scripts and install them on the server, i normally create a folder called /scripts on the root of the drive and put them in there, just remember where you put them as you will need the path later!

Configure the Scripts

Edit the smtp_conf.php file and configure the relevant info:
$host = "mail.example.com";
$username = "user";
$password = "password";
$default_from="server@example.com";

$host = smtp server
$username = account to auth with
$password = account password
$default_from = from account to use if none is specified by the call ( ie presstore !)

Configure PresStore

Edit the client settings in PresStore and specify the external script as the mail sender:

Edit the user settings and specify the email address to send to:

Test It

Click apply and the click the Send Test email button, if all is well you will get an email!
If not, check the php error log. This will either be the log file set in php.ini, the apache error log, or the system log depending on your configuration!


Sunday, 21 October 2012

Resourcespace Plugins

After lots of testing, the ldapauth and view_in_finder plugins have now been incorporated into release 4.6 of the main Resourcespace code.

Monday, 30 July 2012

View In Finder Plugin - New Version

I've updated the view in finder plugin to give the option to respect the access permissions.

If you tick the new check box and set the restrict permissions in group management in team centre, the plugin will now check to see if the user has download access to the resource, and will only display the plugin if they are allowed to download.

You can get the new version here:

View In Finder 2

Wednesday, 14 March 2012

OSX 10.6.8 Weird Network Problem

OK, so I've had this problem for some time on two Mac Book Pro's.
When I put them to sleep by closing the lid, and then wake them up by opening the lid, all GUI networking stops working and doesn't work until the machine is restarted.

So tonight this annoyed me too much and I decided to investigate.
This is the diagnosis I've done so far.. it points to mDNS, surprise surprise! They are both running 10.6.8 and have had all firmware updates etc applied. Although they were manufactured about a year apart, they both have Broadcom 57765-B0 network cards.

The logs show this:

Mar 14 23:12:43  com.apple.SystemStarter[36]: 2012-03-14 23:12:43,366  INFO [RuntimeAnalysisMonitor] [] - Memory Analysis (runtime)>> Allocated: 29256 kb >> Free: 9270 kb >> Max: 126912 kb
Mar 14 23:12:43  mDNSResponder[43]: *** process 43 exceeded 500 log message per second limit  -  remaining messages this second discarded ***
Mar 14 23:12:47: --- last message repeated 2 times ---
Mar 14 23:12:46  mDNSResponder[43]: ERROR: mDNSPlatformReadTCP - recv 74 got CLOSED 435000 times
Mar 14 23:12:46 com.apple.SystemStarter[36]: 2012-03-14 23:12:46,885  INFO [RuntimeAnalysisMonitor] [] - Memory Analysis (runtime)>> Allocated: 63424 kb >> Free: 40257 kb >> Max: 63424 kb
Mar 14 23:12:46  com.apple.SystemStarter[36]: 2012-03-14 23:12:46,978  INFO [Live Status Collector thread] [] - Cleaning all expired Live Status messages
Mar 14 23:12:47 mDNSResponder[43]: *** process 43 exceeded 500 log message per second limit  -  remaining messages this second discarded ***
Mar 14 23:12:54: --- last message repeated 6 times ---
Mar 14 23:12:54  iChatAgent[349]: [Warning] IMDServiceSession <AIMServiceSession: 0x100123700>: Want to connect to slogin.oscar.aol.com, but it is NOT up
Mar 14 23:12:54  mDNSResponder[43]: *** process 43 exceeded 500 log message per second limit  -  remaining messages this second discarded ***

But the following tests work:

nslookup www.bbc.co.uk
Server: 192.168.3.19
Address: 192.168.3.19#53

Non-authoritative answer:
www.bbc.co.uk canonical name = www.bbc.net.uk.
Name: www.bbc.net.uk
Address: 212.58.244.71

telnet 212.58.244.71 80
Trying 212.58.244.71...
Connected to 212.58.244.71.
Escape character is '^]'.
quit
HTTP/1.0 400 Bad request
Content-Type: text/html

<h2>Client sent a bad request.</h2>
Connection closed by foreign host.


Now.. this shows that networking is up, and we can connect to port 80 on a webserver, ie we can surf the web.... 
But everything in the GUI fails to connect to networking.. ARD, afp, web, mail, ical etc.

The solution is to force quit the mDNSResponder either from the command line with:

sudo killall mDNSResponder

or to select the mDNSResponder process in Activity Monitor and force quit it.

Because it's managed by launchd it will automatically relaunch, and when it does networking is instantly restored.

I haven't yet worked out why it goes wrong, but at least I know where the problem is...

Next step is to diagnose the wake up process!

Tuesday, 6 March 2012

Long time no update

When I started this blog I intended to update it regularly like I guess most people who start blogs do!
However, it's been a busy couple of months and I haven't posted anything, which I view as somewhat of a failure on my part.

This doesn't mean I haven't had anything to blog about, just not enough time given other commitments to actually sit down and write stuff. It sometimes amazes me when I think about how much time people must spend writing blogs!

So what I have been up to?
For a start, a lot of time has been spent on house maintenance. It's amazing how many things you can find wrong with a 200 year old house when you start looking! Staircases about to fall down, leaking baths, dodgy electrical installations, the list goes on.
I now have a new moral to this... don't start looking!

From the tech side, I've been doing a lot of stuff with Cat DV recently, and some more with ResourceSpace, so I'll try and get round to adding content very soon.

Hopefully the house renovations are on hold for a while, but you never know what's round the corner.  

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.