EPrints Technical Mailing List Archive

Message: #07129


< Previous (by date) | Next (by date) > | < Previous (in thread) | Next (in thread) > | Messages - Most Recent First | Threads - Most Recent First

[EP-tech] <epc:list>


So, today I've been refactoring our citation file as it was a mess. I split it into one citation per type and made some more citation files for the more complex fields.

That worked really well, but there was still an issue that lots of cases needed me to render a comma separated list of fields where some of them may not be set but you don't want to have "Thing 1, , , Thing 4.". Doubled up commas are hard to avoid.

So, I wrote a handy new feature. This works like this:

 <epc:list join=", " suffix=".">
    <epc:item><epc:if test="thing_a"><epc:print expr="thing_a" /></epc:if></epc:item>     <epc:item><epc:if test="thing_b"><epc:print expr="thing_b" /></epc:if></epc:item>     <epc:item><epc:if test="thing_c"><epc:print expr="thing_c" /></epc:if></epc:item>     <epc:item><epc:if test="thing_d"><epc:print expr="thing_d" /></epc:if></epc:item>
</epc:list>

This will render each list item and only keep it if it contains characters other than whitespace and tags. so "<span> </span>" will be skipped but "Foo" will be included.

It uses the  join text to join each non-empty list item and if there are one or more non-empty items, it puts the suffix and prefix text at the start and end. You can also use first-join and last-join to use different link text between the first or last pair.

This feature can be added to current EPrints by overriding a subroutine. It's brutal, but works. It may require review after a major upgrade. (or maybe someone will add the feature to the next release!)

This is new and not very tested code, so at your own risk etc.



{
no warnings 'redefine';
*EPrints::XML::EPC::process = sub {
	my( $node, %params ) = @_;

	if( !defined $node )
	{
		EPrints::abort( "no node passed to epc process" );
	}
# cjg - Potential bug if: <ifset a><ifset b></></> and ifset a is disposed
# then ifset: b is processed it will crash.

	if( EPrints::XML::is_dom( $node, "Element" ) )
	{
		my $name = $node->tagName;
		$name =~ s/^epc://;

		return $params{session}->xml->create_document_fragment
			if $node->hasAttribute( "disabled" ) && $node->getAttribute( "disabled" );

		if( $name=~m/^(if|comment|choose|print|debug|phrase|pin|foreach|set|list)$/ )
		{
			my $fn = "EPrints::XML::EPC::_process_$name";
			no strict "refs";
			my $r = eval { &{$fn}( $node, %params ); };
			use strict "refs";
			if( $@ )
			{
				$params{session}->log( "EPScript error: $@" );
				return $params{session}->html_phrase( "XML/EPC:script_error" );
			}
			return $r;
		}
	}

	my $collapsed = $params{session}->clone_for_me( $node );
	my $attrs = $collapsed->attributes;
	if( defined $attrs )
	{
		for( my $i = 0; $i<$attrs->length; ++$i )
		{
			my $attr = $attrs->item( $i );
			my $v = $attr->nodeValue;
			my $name = $attr->nodeName;
			my $newv = eval { EPrints::XML::EPC::expand_attribute( $v, $name, \%params ); };
			if( $@ )
			{
				$params{session}->log( "EPScript error: $@" );
				$newv = $params{session}->phrase( "XML/EPC:script_error" );
			}
			if( $v ne $newv ) { $attr->setValue( $newv ); }
		}
	}

	if( $node->hasChildNodes )
	{
		$collapsed->appendChild( EPrints::XML::EPC::process_child_nodes( $node, %params ) );
	}
	return $collapsed;
}
} # end of block that disabled 'redefine' warnings


sub EPrints::XML::EPC::_process_list
{
	my( $node, %params ) = @_;

	# if there's only 2 items and first & last join are defined then last join is used.
	my $opts = {};
	foreach my $atr ( qw/ join suffix prefix first-join last-join / )
	{
		if( $node->hasAttribute( $atr ) )
		{
			$opts->{$atr} = $node->getAttribute( $atr );
		}
	}

	my @out_nodes = ();
	foreach my $child ( $node->getChildNodes )
	{
		next unless( EPrints::XML::is_dom( $child, "Element" ) );
		my $name = $child->tagName;
		$name=~s/^ep://;
		$name=~s/^epc://;
		if( ! $name eq "item" )
		{
			EPrints::abort( "In ".$params{in}.": only epc:item is allowed in epc:list.\n".substr( $child->toString, 0, 100 ) );
		}
		
		my $collapsed_item = EPrints::XML::EPC::process_child_nodes( $child, %params );
		
		# Add result to the output list IF it's not just whitespace
		my $text = $params{session}->xml->to_string( $collapsed_item );
		$text =~ s/\s//g;
		if( $text ne "" )
		{
			push @out_nodes, $collapsed_item;
		}
	}

	my $result = $params{session}->make_doc_fragment;
	if( scalar @out_nodes > 0 )
	{
		# at least one item!
		if( defined $opts->{prefix} )
		{
			$result->appendChild( $params{session}->make_text( $opts->{prefix} ) );
		}
		for( my $pos=0; $pos < scalar @out_nodes; ++$pos )
		{
			my $join;
			if( $pos > 0 ) {
				$join = $opts->{join};
				if( $pos == 1 && defined $opts->{"first-join"} ) { $join = $opts->{"first-join"}; }
				if( $pos == ((scalar @out_nodes)-1) && defined $opts->{"last-join"} ) { $join = $opts->{"last-join"}; }
			}
			if( defined $join )
			{
				$result->appendChild( $params{session}->make_text( $join ) );
			}
			$result->appendChild( $out_nodes[$pos] );
		}
		if( defined $opts->{suffix} )
		{
			$result->appendChild( $params{session}->make_text( $opts->{suffix} ) );
		}
	}
				
	return $result;
}



--
Christopher Gutteridge -- http://users.ecs.soton.ac.uk/cjg

University of Southampton Open Data Service: http://data.southampton.ac.uk/
You should read our Web & Data Innovation blog: http://blogs.ecs.soton.ac.uk/webteam/