package votorola.a.web; // Copyright 2008, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
import java.io.*;
import java.sql.*;
import java.util.List;
import java.util.logging.*; import votorola.g.logging.*;
import org.apache.wicket.*;
import org.apache.wicket.markup.html.*;
import org.apache.wicket.markup.html.basic.*;
import org.apache.wicket.markup.html.panel.*;
import org.apache.wicket.markup.repeater.*;
import votorola._.*;
import votorola.a.*;
import votorola.a.election.*;
import votorola.a.locale.*;
import votorola.a.register.RegistrationC;
import votorola.a.register.WP_Register;
import votorola.a.voter.*;
import votorola.g.hold.*;
import votorola.g.io.*;
import votorola.g.lang.*;
import votorola.g.servlet.*;
import votorola.g.util.*;
/** A meta-page in the Web interface - to provide bootstrap instructions
* on accessing the electoral services, as well as general information
* about the subserver.
*
* @see ../design.xht#meta-service
* @see
* WP_Meta.html
*/
@ThreadRestricted("wicket")
public final class WP_Meta extends VPage implements TabbedPage // public - bookmarkable page
{
/** Constructs a WP_Meta.
*/
public WP_Meta() throws SQLException // public - bookmarkable page, must have default|PageParameter constructor
{
final VRequestCycle cycle = VRequestCycle.get();
final BundleFormatter bun = cycle.bunW();
final ApplicationScope appScope = cycle.vApplication().scopeMeta();
add( new WC_NavigationControls( "navSuperBar", WP_Meta.this, cycle ));
add( new WC_NavPile( "navPile", navTab(cycle), cycle ));
// Title and Summary Description
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{
if( cycle.vApplication().getCustomXHTML(XHTML_PRETITLE) != null )
{
add( newCustomLabel( "preTitle", XHTML_PRETITLE ));
}
else add( newNullComponent( "preTitle" ));
ElectoralSubserver subserver = cycle.vApplication().subserverRun().subserver();
add( new Label( "title", subserver.title() ));
add( new Label( "titleH", subserver.title() ));
add( new Label( "summaryDescription", subserver.summaryDescription() ));
}
// Activity Window - showing what other voters were recently doing
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
add( new Label( "eventHeader-lapse", bun.l( "a.web.WP_Meta.eventHeader-lapse" )));
add( new Label( "eventHeader-message", bun.l( "a.web.WP_Meta.eventHeader-message" )));
{
final RepeatingView repeating = new RepeatingView( "eventData" );
synchronized( appScope.activityList() )
{
for( final ActivityEvent event: appScope.activityList() )
{
final WebMarkupContainer tr = new WebMarkupContainer( repeating.newChildId() );
// time lapse
// ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `
tr.add( new Label( "eventData-lapse", event.lapseToString( bun )));
// message
// ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `
tr.add( newMessageComponent( "eventData-message", event, cycle ));
repeating.add( tr );
}
}
add( repeating );
}
// Version
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
add( new Label( "version", "v. " + Votorola.VERSION ));
}
// ````````````````````````````````````````````````````````````````````````````````````
// Initialized early, for use in other initializers.
/** The left-hand navigation tab in the subserver bar, linking to the subserver page
* (instance of WP_Meta).
*/
static final NavTab NAV_TAB = new NavTab()
{
private final Bookmark bookmark = new Bookmark( WP_Meta.class );
public @Override Bookmark bookmark() { return bookmark; }
public String shortTitle( VRequestCycle cycle )
{
return cycle.bunW().l( "a.web.WP_Meta.tab.shortTitle" );
}
};
// ------------------------------------------------------------------------------------
/** The bar for navigating among the subserver pages.
*/
public static NavBar navBar() { return navBar; }
private static final NavBar navBar = new NavBar()
{
public SuperTab superTab() { return superTab; } // "Toronto", i.e. whatever district the office serves
private final SuperTab superTab = new SuperTab( this )
{
public String shortTitle( VRequestCycle cycle )
{
return cycle.vApplication().subserverRun().subserver().shortTitle();
}
};
public List tabList() { return tabList; }
private final ArrayListU tabList = new ArrayListU( new NavTab[]
{
NAV_TAB.setNavBar( this ),
WP_Help.NAV_TAB.setNavBar( this )
});
};
/** Key for a custom preface to the title (h1 or h2 element).
*
* @see VApplication#putCustomXHTML(String,String)
*/
public static final String XHTML_PRETITLE = WP_Meta.class.getName() + "/preTitle";
// - T a b b e d - P a g e ------------------------------------------------------------
/** @see #NAV_TAB
*/
public NavTab navTab( VRequestCycle cycle ) { return NAV_TAB; }
// ====================================================================================
/** Application scope for instances of WP_Meta.
*
* @see VApplication#scopeMeta()
*/
public static @ThreadSafe final class ApplicationScope
{
/** Constructs an ApplicationScope.
*/
ApplicationScope( final VApplication app )
{
final File activityListFile =
new File( app.cacheDirectory(), "web-activity-list.serial" );
ActivityList.TL list = null;
try
{
list = (ActivityList.TL)FileX.readObject( activityListFile );
}
catch( Exception x )
{
if( x instanceof RuntimeException ) throw (RuntimeException)x;
LoggerX.i(WP_Meta.class).config( "unable to restore activity list from previous run: " + x.toString() );
}
final long lastRunShutdownTime; // or 0L if unknown
if( list == null )
{
activityList = new ActivityList.TL();
lastRunShutdownTime = 0L;
}
else
{
activityList = list;
lastRunShutdownTime = activityListFile.lastModified();
}
activityList.log( new StartEvent( lastRunShutdownTime ));
app.spool().add( new Hold()
{
public @ThreadSafe void release()
{
synchronized( activityList )
{
try
{
FileX.writeObject( activityList, activityListFile );
}
catch( IOException x ) { LoggerX.i(WP_Meta.class).config( "unable to persist activity list to file: " + x.toString() ); }
}
}
});
}
// --------------------------------------------------------------------------------
@ThreadRestricted("holds activityList")
public ActivityList.TL activityList() { return activityList; }
private final ActivityList.TL activityList;
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
private Component newMessageComponent( final String id, final ActivityEvent event,
final VRequestCycle cycle ) throws SQLException
{
final BundleFormatter bun = cycle.bunW();
if( event instanceof Vote.VotingEvent )
{
final Vote.VotingEvent votingEvent = (Vote.VotingEvent)event;
final ElectoralSubserver.Run run = cycle.vApplication().subserverRun();
final ElectoralService serviceOrNull =
run.electoralService( votingEvent.electionName() ); // as provided on this run
final String candidateDisplayName = VSession.displayName(
new RegistrationC( votingEvent.candidateEmail(), run.register().voterInputTable() ));
final String electionTitle;
if( serviceOrNull instanceof Election ) electionTitle = serviceOrNull.title();
else electionTitle = "Unprovided Service"; // rare case
final String voterDisplayName = VSession.displayName(
new RegistrationC( votingEvent.voterEmail(), run.register().voterInputTable() ));
if( event instanceof Vote.CastEvent )
{
final Fragment fragment = new Fragment( id, "frag-voteCastEvent", WP_Meta.this );
fragment.add( new Label( "voteCastEvent-a",
bun.l( "a.web.WP_Meta.voteCastEvent-a(1,2,3)",
candidateDisplayName, electionTitle, voterDisplayName )));
final BookmarkablePageLinkX link = new WP_RoleV.RoleLink
( "voteCastEvent-action", votingEvent.electionName(), votingEvent.voterEmail() );
link.setBodyModel( bun.l( "a.web.WP_Meta.voteCastEvent-actionLinkBody(1,2,3)",
candidateDisplayName, electionTitle, voterDisplayName ));
link.setEnabled( serviceOrNull instanceof Election );
fragment.add( link );
fragment.add( new Label( "voteCastEvent-b",
bun.l( "a.web.WP_Meta.voteCastEvent-b(1,2,3)",
candidateDisplayName, electionTitle, voterDisplayName )));
return fragment;
}
else if( event instanceof Vote.WithdrawalEvent )
{
final Fragment fragment = new Fragment( id, "frag-voteWithdrawalEvent", WP_Meta.this );
fragment.add( new Label( "voteWithdrawalEvent-a",
bun.l( "a.web.WP_Meta.voteWithdrawalEvent-a(1,2,3)",
candidateDisplayName, electionTitle, voterDisplayName )));
final BookmarkablePageLinkX link = new WP_RoleV.RoleLink
( "voteWithdrawalEvent-action", votingEvent.electionName(), votingEvent.voterEmail() );
link.setBodyModel( bun.l( "a.web.WP_Meta.voteWithdrawalEvent-actionLinkBody(1,2,3)",
candidateDisplayName, electionTitle, voterDisplayName ));
link.setEnabled( serviceOrNull instanceof Election );
fragment.add( link );
fragment.add( new Label( "voteWithdrawalEvent-b",
bun.l( "a.web.WP_Meta.voteWithdrawalEvent-b(1,2,3)",
candidateDisplayName, electionTitle, voterDisplayName )));
return fragment;
}
else assert false;
}
return new Label( id, event.description( bun )); // default
}
// ====================================================================================
private static class StartEvent extends ActivityEvent
{
private static final long serialVersionUID = 0L;
private StartEvent( long lastRunShutdownTime )
{
this.lastRunShutdownTime = lastRunShutdownTime;
}
private final long lastRunShutdownTime; // or 0L if unknown
// - A c t i v i t y - E v e n t --------------------------------------------------
public final @Override String description( BundleFormatter bun )
{
if( lastRunShutdownTime > 0L )
{
return bun.l( "a.web.WP_Meta.startEvent(1)",
lapseToString( lastRunShutdownTime, timestamp(), bun ));
}
else return bun.l( "a.web.WP_Meta.startEvent" );
}
}
}