I’m now working on building an archive catalogue for clients of Docuracy Ltd. Attempts have been made before to bring order to what is a rather disparate collection, which includes documents which date back many hundreds of years.

Imagine my delight on discovering that an open-source, standards-compliant archive description system is freely available: ICA-AtoM. The remaining challenges were:
- to identify and set up a Virtual Private Server capable of the necessary configuration;
- installation of the ICA-Atom software;
- to build a system to convert the catalogue’s previous word-processor files into XML conformed to ICA-AtoM’s import requirements;
- to convince ourselves and our clients that the system is superior to commercially available software on grounds other than lack-of-cost!
This post is prompted by some work I did today on challenge 4: the ICA-AtoM system is extremely powerful and offers very complex record searching capabilities. The proposed end-users of the system, however, are not necessarily computer-literate, and it seemed desirable to present some of the complex search-functionality in a more user-friendly way than the standard set-up permits.
I’ve built a plug-in, the “Docuracy Query Helper” which through JQuery helps with the construction of complicated catalogue queries – what amounts on other systems to an “Advanced Search” tab. It’s designed to be unobtrusive, and doesn’t interfere in any way with the normal operation of the software.
If you’d like to try it out, all you need to do is replace a single file in your ICA-AtoM installation:
<ica-atom-root>/apps/qubit/modules/search/templates/_box.php
Please make sure you back up the original file first by renaming it, then create a new file with the original name, and cut-and-paste into it the code from the box below. The <style> section is, incidentally, a convenient place to try out some basic style customisation – on the line beginning “div.search”.
<div class="search section">
<h2 class="element-invisible"><?php echo __('Search') ?></h2>
<div class="content">
<style type="text/css">
div#qHelper {text-align:right;}
div#qHelper span#qSwitch{color:#3366CC;cursor:pointer;}
div#qHelper div#qPanel,div#qHelper div#qSwitch {display:none;}
div#qHelper input[type=text] {width:272px;margin:3px 0 3px 5px;}
div#qHelper span#qItemsOnlySpan {float:left;border:1px solid black;padding:3px;margin:3px 5px 0 0;}
div#qHelper input[type=checkbox] {width:auto;margin:3px 0 3px 5px;position:relative;top:2px;}
div#qHelper select {width:55px;font-size:9px;margin:3px 5px 3px 0;padding:1px;}
div#qHelper select#qEASpelling,div#qHelper select#qEAExcludeSpelling, div#qHelper select#qLimitType {width:68px;}
div#qHelper input#qDate1,div#qHelper input#qDate2 {width:60px;font-style:italic;color:#999;}
div#qManual {clear:both;font-size:9px;}
div.search {background-image:url(http://docuracy.co.uk/images/ica-docuracy.png)!important;background-repeat:no-repeat!important;background-position:10px 10px!important;}
</style>
<form action="<?php echo url_for(array('module' => 'search')) ?>">
<input name="query" value="<?php echo esc_entities($sf_request->query) ?>"/>
<input class="form-submit" type="submit" value="<?php echo __('Search') ?>"/>
<div id="qHelper">
<span id="qSwitch"><span id="qSwitchHide">Hide </span>Docuracy Query Helper</span>
<div id="qPanel">
<label for="qAllWords">Find <b>all</b> these words, </label><select id="qEASpelling"><option selected="selected">EXACT</option><option>LOOSE</option></select>spelling:<input type="text" id="qAllWords" autocomplete="off" /><br />
<select id="qAOExactPhrase"><option selected="selected">AND</option><option>OR</option></select><label for="qExactPhrase">this <b>exact</b> phrase:</label><input type="text" id="qExactPhrase" autocomplete="off" /><br />
<select id="qAOOneOrMore"><option selected="selected">AND</option><option>OR</option></select><label for="qOneOrMore"><b>one or more</b> of these words:</label><input type="text" id="qOneOrMore" autocomplete="off" /><br />
<select id="qAONear"><option selected="selected">AND</option><option>OR</option></select><label for="qNear">these words <b>near</b> each other:</label><input type="text" id="qNear" autocomplete="off" /><br />
<label for="qExcludeWords"><b>Exclude</b> words, </label><select id="qEAExcludeSpelling"><option selected="selected">EXACT</option><option>LOOSE</option></select>spelling:<input type="text" id="qExcludeWords" autocomplete="off" /><br />
<span id="qItemsOnlySpan"><label for="qItemsOnly">Limit results to </label><select id="qLimitType"><option selected="selected" value="levelofdescription:item">Items</option><option value="mediatype:image">Images</option></select><b>only</b>:<input type="checkbox" id="qItemsOnly" /></span>
<label for="qDate1"><b>Date: </label><input type="text" id="qDate1" /><label for="qDate2"><b> <i>to</i> </b></label><input type="text" id="qDate2" title="May be left blank to search for exact date."/><br />
<div id="qManual">For a full description of other search options, <a href="http://framework.zend.com/manual/en/zend.search.lucene.query-language.html" title="Lucene Search Options" target="_blank">click here</a>.</div>
</div>
</div>
</form>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" type="text/javascript"></script>
<script language="javascript">
function qUpdateQuery(){
function brackets(qElement){
return ((multipleInputs)?("("+qElement+")"):(qElement));
}
var query = new Array();
var multipleInputs = ($('div#qHelper input:text[value!=""]').filter('input:text[value!="YYYY"]').length>1);
var qTest = $('div#qHelper input#qAllWords').val();
if(qTest!=''){
if($('div#qHelper select#qEASpelling').val()=='LOOSE'){
qTest = $.trim(qTest).replace(/\s+/g,"~ ")+"~";
}
qTest = $.trim(qTest).replace(/\s+/g," AND ");
query.push(brackets(qTest));
}
var qTest = $('div#qHelper input#qExactPhrase').val();
if(qTest!=''){
qTest=brackets('"'+qTest+'"');
if(query.length>0)qTest=$('div#qHelper select#qAOExactPhrase').val()+" "+qTest;
query.push(qTest);
}
var qTest = $('div#qHelper input#qOneOrMore').val();
if(qTest!=''){
qTest = $.trim(qTest).replace(/\s+/g," OR ");
qTest=brackets('"'+qTest+'"');
if(query.length>0)qTest=$('div#qHelper select#qAOOneOrMore').val()+" "+qTest;
query.push(qTest);
}
var qTest = $('div#qHelper input#qNear').val();
if(qTest!=''){
qTest=brackets('"'+qTest+'"~5');
if(query.length>0)qTest=$('div#qHelper select#qAONear').val()+" "+qTest;
query.push(qTest);
}
var qTest = $('div#qHelper input#qExcludeWords').val();
if((qTest!='')&&(query.length>0)){
if($('div#qHelper select#qEAExcludeSpelling').val()=='LOOSE'){
qTest = $.trim(qTest).replace(/\s+/g,"~ ")+"~";
}
query.push("AND NOT "+brackets(qTest));
}
query = query.join(' ');
var qDates = new Array();
$('div#qHelper input[id^="qDate"]').each(function(){
var date = $(this).val();
if((date!='YYYY')&&(date!='')&&(!date.match(/^[1-2][0-9]{3}$/))){
$(this).css('border-color','red').attr('title','Invalid year entered.');
}
else{
$(this).css({'border-color':'#ccc','border-top-color':'#999'}).attr('title','');
}
if((date.match(/^[1-2][0-9]{3}$/))&&(query!=''))qDates.push(date);
});
if(qDates.length>0)query = 'dates:['+qDates.join(' TO ')+'] AND ('+query+')';
if(($('#qItemsOnly:checked').length>0)&&(query!=''))query = $('div#qHelper select#qLimitType').val()+' AND ('+query+')';
$('div.search input[name="query"]').val(query);
}
$('#qSwitch').show().click(function(){$('#qPanel,#qSwitchHide').toggle()});
$('#qSwitchHide').hide();
$('div#qHelper input[id^="qDate"]').val('YYYY').click(function(){$(this).val('').unbind('click').css({'color':'#000','font-style':'normal'});});
$('div#qHelper input:text').bind('keyup',qUpdateQuery);
$('div#qHelper select, div#qHelper input[type!="text"]').bind('change',qUpdateQuery);
</script>
</div>
</div>