001package votorola.a.voter; // Copyright 2008-2012, 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. 002 003import javax.mail.internet.*; 004import org.apache.wicket.*; 005import org.apache.wicket.request.mapper.parameter.PageParameters; 006import votorola.a.web.wic.*; 007import votorola.g.*; 008import votorola.g.lang.*; 009import votorola.g.mail.*; 010import votorola.g.web.wic.*; 011 012import static votorola.a.voter.IDPair.NOBODY; 013 014 015/** A page that is navigable by user. The particular user to display is specified by 016 * query parameter 'u' or 'v'. All instances of VoterPage support the following query 017 * parameters: 018 * 019 * <table class='definition' style='margin-left:1em'> 020 * <tr> 021 * <th class='key'>Key</th> 022 * <th>Value</th> 023 * <th>Default</th> 024 * </tr> 025 * <tr><td class='key'>u</td> 026 * 027 * <td>The {@linkplain IDPair#username() username} of the voter. Any {@linkplain 028 * MediaWiki#demiEncodedPageName(String,boolean) demi-encoding} with underscores 029 * in place of spaces is automatically decoded. Incompatible with parameter 'v'; 030 * specify one or the other.</td> 031 * 032 * <td>Null, specifying no particular user.</td> 033 * 034 * </tr> 035 * <tr><td class='key'>v</td> 036 * 037 * <td>The {@linkplain IDPair#email() email address} of the user. Incompatible 038 * with parameter 'u'; specify one or the other.</td> 039 * 040 * <td>Null, specifying no particular user.</td> 041 * 042 * </tr> 043 * </table> 044 */ 045public interface VoterPage 046{ 047 048 049 // - V o t e r - P a g e -------------------------------------------------------------- 050 051 052 /** Identifies the voter by canonical email address. 053 * 054 * @see #voterIDPair() 055 */ 056 public String voterEmail(); 057 058 059 060 /** Identifies either the voter who is viewed on this page, or {@linkplain 061 * IDPair#NOBODY nobody}. 062 * 063 * @see #voterEmail() 064 * @see #voterUsername() 065 */ 066 public IDPair voterIDPair(); 067 068 069 070 /** The voter's username as derived from the email address. 071 * 072 * @see #voterIDPair() 073 */ 074 public String voterUsername(); 075 076 077 078 // ==================================================================================== 079 080 081 /** Session scope for instances of VoterPage. 082 * 083 * @see VSession#scopeVoterPage() 084 */ 085 public static @ThreadSafe final class SessionScope implements java.io.Serializable 086 { 087 088 private static final long serialVersionUID = 0L; 089 090 091 092 /** Constructs a SessionScope. 093 */ 094 public SessionScope( VSession session ) { this.session = session; } 095 096 097 098 private final VSession session; 099 100 101 102 // -------------------------------------------------------------------------------- 103 104 105 /** Identifies either the last voter fore-navigated to, or {@linkplain 106 * IDPair#NOBODY nobody}. 107 * 108 * @see #setLastIDPair(IDPair) 109 */ 110 public IDPair getLastIDPair() { return lastIDPair; } 111 112 113 private volatile IDPair lastIDPair = NOBODY; 114 115 116 117 /** Sets the identity of the last voter fore-navigated to. Setting it to 118 * {@linkplain IDPair#NOBODY NOBODY} will clear it to default. 119 * 120 * @see #getLastIDPair() 121 */ 122 public void setLastIDPair( final IDPair newLastIDPair ) 123 { 124 if( newLastIDPair.equals( lastIDPair )) return; // or throw NullPointerException 125 126 lastIDPair = newLastIDPair; 127 if( !setLastIDPair_done && !NOBODY.equalsEmail( newLastIDPair )) 128 { 129 setLastIDPair_done = true; 130 } 131 132 session.dirty(); // per Session API 133 } 134 135 136 private volatile boolean setLastIDPair_done; 137 138 139 140 /** The ID pair of either the last voter fore-navigated to, or the authenticated user, 141 * or {@linkplain IDPair#NOBODY NOBODY}. The return value defaults to the user 142 * only if no voter has ever been navigated to. Once a voter has been navigated 143 * to, this method will default directly to NOBODY. 144 */ 145 public IDPair getLastVoterOrUser() 146 { 147 IDPair idPair = lastIDPair; // snapshot copy, for atomic test/return 148 if( !setLastIDPair_done && NOBODY.equalsEmail( idPair )) 149 { 150 idPair = session.userOrNobody(); 151 } 152 return idPair; 153 } 154 155 156 157 // /** The ID pair of either the currently displayed voter; or the last 158 // * fore-navigated to. If the current page is a voter page, and is displaying a 159 // * voter, then the voter is returned. Otherwise, the value of 160 // * getLastVoterOrUserEmail() is returned. The values differ only if the user has 161 // * back-navigated to the current page, which would not be reflected in 162 // * getLastVoterOrUserEmail(). 163 // */ 164 // public IDPair lastVoterDisplayed( final VRequestCycle cycle ) 165 // { 166 // final Page page = cycle.responsePage(); 167 // IDPair idPair = NOBODY; 168 // if( page instanceof VoterPage ) idPair = ((VoterPage)page).voterIDPair(); 169 // 170 // if( NOBODY.equalsEmail( idPair )) idPair = getLastVoterOrUser(); 171 // 172 // return idPair; 173 // } 174 // No clients anymore, and VRequestCycle.responsePage() needs work as of 1.5. 175 176 177 } 178 179 180 181 // ==================================================================================== 182 183 184 /** Voter page utilities. 185 */ 186 public static @ThreadRestricted("wicket") final class U 187 { 188 189 private U() {} 190 191 192 /** Extracts the ID pair corresponding to query parameter 'u' or 'v' if either is 193 * specified; otherwise returns {@linkplain IDPair#NOBODY}. 194 * 195 * @throws RestartResponseException redirecting to an error message page if 196 * both 'u' and 'v' are specified, or either is malformed. 197 */ 198 public static IDPair idPairOrNobodyFor( final PageParameters p ) 199 { 200 final IDPair idPair; 201 final String username = p.get( "u" ).toString(); 202 final String email = p.get( "v" ).toString(); 203 try 204 { 205 if( username == null ) 206 { 207 if( email == null ) idPair = IDPair.NOBODY; 208 else idPair = IDPair.fromEmail( InternetAddressX.canonicalAddress( email )); 209 } 210 else 211 { 212 if( email != null ) 213 { 214 VSession.get().error( "both parameters 'u' and 'v' specified" ); 215 throw new RestartResponseException( new WP_Message() ); 216 } 217 218 idPair = IDPair.fromUsername( MediaWiki.demiDecodedPageName( username )); 219 /// not actually necessary, has no effect on result [not true] 220 // idPair = IDPair.fromUsername( username ); 221 } 222 } 223 catch( AddressException x ) 224 { 225 VSession.get().error( x.toString() ); 226 throw new RestartResponseException( new WP_Message() ); 227 } 228 229 return idPair; 230 } 231 232 233 234 /** Sets the appropriate identifier parameter ('u' or 'v') as specified by the ID 235 * pair, and clears the other parameter; or clears both if ID pair is {@linkplain 236 * IDPair#NOBODY NOBODY}. If p is null, and a value needs to be set, then p is 237 * automatically constructed and returned. 238 * 239 * @param p the parameters, which may be null. 240 * @return the same page parameters. 241 */ 242 @SuppressWarnings("deprecation") 243 public static PageParameters setFrom( final IDPair idPair, PageParameters p ) 244 { 245 if( IDPair.NOBODY.equalsEmail( idPair )) 246 { 247 if( p != null ) 248 { 249 p.remove( "u" ); 250 p.remove( "v" ); 251 } 252 } 253 else if( idPair.isFromEmail() ) 254 { 255 if( p != null ) p.remove( "u" ); 256 p = PageParametersX.withSet( p, "v", idPair.email() ); 257 } 258 else 259 { 260 if( p != null ) p.remove( "v" ); 261 p = PageParametersX.withSet( p, "u", idPair.username() ); 262 } 263 return p; 264 } 265 266 267 268 /** Returns the specified page parameters (pP) with a value for the identifier 269 * parameter ('u' or 'v') as recalled from the session. If a value is recalled 270 * but pP is null, then pP is automatically constructed. 271 * 272 * @param pP the parameter map, which may be null. 273 */ 274 public static PageParameters withRecall_u_v( final PageParameters pP ) 275 { 276 return setFrom( VSession.get().scopeVoterPage().getLastIDPair(), pP ); 277 } 278 279 280 281 } 282 283 284 285}