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}