001package votorola.s.wic; // Copyright 2011-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 com.sun.jersey.api.uri.*;
004import java.io.*;
005import javax.xml.stream.*;
006import org.apache.wicket.*;
007import org.apache.wicket.markup.html.basic.*;
008import org.apache.wicket.markup.html.form.*;
009import org.apache.wicket.markup.html.link.*;
010import org.apache.wicket.markup.html.panel.*;
011import org.apache.wicket.model.*;
012import org.apache.wicket.request.mapper.parameter.PageParameters;
013import votorola.a.*;
014import votorola.a.count.*;
015import votorola.a.position.*;
016import votorola.a.voter.*;
017import votorola.a.web.wic.*;
018import votorola.a.web.wic.authen.*;
019import votorola.g.lang.*;
020import votorola.g.web.wic.*;
021import votorola.s.wic.count.*;
022
023
024/** A page that redirects to the user's draft, or, if there is no draft, guides the user
025  * in creating one.  It supports <a href='WP_Draft.html#devMode'>GWT dev mode</a> and the
026  * <a href='WP_Draft.html#xf'>relaying of state</a> between Crossforum Theatre pages.  An
027  * example request is:
028  *
029  * <blockquote><code><a href="http://reluk.ca:8080/v/w/MyDraft?p=Sys!p!sandbox" target='_top'>http://reluk.ca:8080/v/w/MyDraft?p=Sys!p!sandbox</a></code></blockquote>
030  *
031  * <h3>Query parameters</h3>
032  *
033  * <table class='definition' style='margin-left:1em'>
034  *     <tr>
035  *         <th class='key'>Key</th>
036  *         <th>Value</th>
037  *         <th>Default</th>
038  *         </tr>
039  *     <tr><td class='key'>p</td>
040  *
041  *         <td>The {@linkplain votorola.a.count.Poll#name() poll name}.  Any slash
042  *         characters (/) may be encoded as exclamation marks (!).</td>
043  *
044  *         <td>Null, resulting in a 303 (see other) redirect that fills in the name of
045  *         the {@linkplain votorola.a.count.Poll#TEST_POLL_NAME test poll}.</td>
046  *
047  *         </tr>
048  *     <tr><td class='key'>ru</td>
049  *
050  *         <td>The username of the user in the remote wiki 'rw', if known.</td>
051  *
052  *         <td>None</td>
053  *
054  *         </tr>
055  *     <tr><td class='key'>rw</td>
056  *
057  *         <td>A remote wiki in which to create the draft if it does not exist.  Specify
058  *         the wiki by the URL for its index script, without a trailing slash (/).  For
059  *         example "rw=http://reluk.ca/mediawiki/index.php".  Do not specify the local
060  *         pollwiki here, or a local draft will be created in remote form.</td>
061  *
062  *         <td>None</td>
063  *
064  *         </tr>
065  *     </table>
066  *
067  *     @see <a href='../../../../../a/position/WP_MyDraft.html' target='_top'>WP_MyDraft.html</a>
068  *     @see <a href='http://mail.zelea.com/list/votorola/2011-December/001259.html'>Guided creation of new drafts in the pollwiki</a>
069  */
070  @ThreadRestricted("wicket") @org.apache.wicket.devutils.stateless.StatelessComponent
071public final class WP_MyDraft extends VPageHTML
072{
073
074    // Do not localize this page till draft creation is properly automated, as the content
075    // is too verbose.
076
077
078    /** Constructs a WP_MyDraft.
079      */
080    public WP_MyDraft( final PageParameters pP ) throws IOException, XMLStreamException // bookmarkable page iff constructor public & (default|PageParameter)
081    {
082        super( pP );
083
084        final VRequestCycle cycle = VRequestCycle.get();
085        final VOWicket app = VOWicket.get();
086        final VSession.User user = VSession.get().user();
087        if( user == null )
088        {
089            pP.set( "returnClass", getClass().getName() );
090            throw new RestartResponseException( app.authenticator().newLoginPage( pP ));
091        }
092
093        final String pollName = Poll.U.toName( WP_Poll.maybeRedirect_P( WP_MyDraft.class, pP,
094          cycle ));
095
096      // Either redirect to the user's existing draft ...
097      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
098        final String coreLoc = WP_Draft.maybeRedirectToDraft( user.username(), pollName,
099          /*contextPersonName*/null, app, cycle );
100
101      // ... or explain how to create a new draft.
102      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
103        add( new WC_NavigationHead( "navHead", WP_MyDraft.this, cycle ));
104        final Model<String> titleModel = new Model<String>( "Create a draft" );
105        add( new Label( "title", titleModel ));
106        add( new Label( "h", titleModel ));
107
108        final VoteServer vS = app.vsRun().voteServer();
109        final String remoteWikiIndexScript = stringNonEmpty( pP, "rw" );
110          // We could try to guard here against 'rw' specifying the local wiki, but there
111          // is no easy way to know the canonical URL of the local wiki.  We cannot assume
112          // that vS.pollwiki().scriptURI() is set to anything but an alias.  So instead of
113          // guarding against an erroneous local 'rw' here in the code, we document a rule
114          // against it in the API and let it fail consistently (as documented) if that
115          // rule is broken.
116        if( remoteWikiIndexScript == null ) initLocal( vS, user, coreLoc, pP );
117        else initRemote( vS,pollName, user, coreLoc, remoteWikiIndexScript, pP );
118    }
119
120
121
122    private final @Warning("init call") void initLocal( final VoteServer vS, final IDPair user,
123      final String coreLoc, final PageParameters pP )
124    {
125        final Fragment y = new Fragment( "instruct", "localFrag", WP_MyDraft.this );
126        add( y );
127
128      // Local login.
129      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
130        addLocalLogin( y, user, vS );
131
132      // Local paste.
133      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
134        y.add( newLocalPasteLink( coreLoc ));
135    }
136
137
138
139    private final @Warning("init call") void initRemote( final VoteServer vS,
140      final String pollName, final IDPair user, final String coreLoc,
141      final String remoteWikiIndexScript, final PageParameters pP )
142    {
143        final Fragment y = new Fragment( "instruct", "remoteFrag", WP_MyDraft.this );
144        add( y );
145
146      // Remote username.
147      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
148        final IModel<String> remoteUsername;
149        {
150            final String ru = stringNonEmpty( pP, "ru" );
151            final Model<String> ruInput = new Model<String>( ru );
152            if( ru == null )
153            {
154                final Fragment fragment = new Fragment( "ru", "ruFrag", WP_MyDraft.this );
155                y.add( fragment );
156                fragment.add( new ExternalLink( "remoteWikiLink", remoteWikiIndexScript
157                  + "?title=Special:MyPage", "drafting wiki" ));
158
159                final Form<Void> form = new Form<Void>( "form" );
160                fragment.add( form );
161                form.add( new TextField<String>( "ruInput", ruInput ));
162                form.add( new Button( "reload" ));
163            }
164            else y.add( newNullComponent( "ru" ));
165
166            ruInput.setObject( ru );
167            remoteUsername = new AbstractReadOnlyModel<String>()
168            {
169                public @Override String getObject()
170                {
171                    final String o = ruInput.getObject();
172                    return o == null? "USERNAME": o; // in case user enters nothing
173                }
174            };
175        }
176
177      // Remote login.
178      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179        {
180            final Label label = new Label( "remoteUsername", remoteUsername );
181            y.add( label );
182            label.setRenderBodyOnly( true );
183
184            final Model<String> href = new Model<String>( remoteWikiIndexScript
185              + "?title=Special:UserLogin" );
186            y.add( new ExternalLink( "remoteLoginLink", href, href ));
187        }
188
189      // Local login.
190      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191        addLocalLogin( y, user, vS );
192
193      // Local copy.
194      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
195        final IModel<String> remoteDraftName = new AbstractReadOnlyModel<String>()
196        {
197            public @Override String getObject()
198            {
199                return "User:" + remoteUsername.getObject() + "/" + pollName;
200            }
201        };
202        {
203            final Label label = new Label( "draftPointer-index", remoteWikiIndexScript );
204            y.add( label );
205            label.setRenderBodyOnly( true );
206        }
207        {
208            final Label label = new Label( "draftPointer-page", remoteDraftName );
209            y.add( label );
210            label.setRenderBodyOnly( true );
211        }
212
213      // Local paste.
214      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
215        y.add( newLocalPasteLink( coreLoc ));
216
217      // Remote copy.
218      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
219        {
220            final Label label = new Label( "localUsername", user.username() );
221            y.add( label );
222            label.setRenderBodyOnly( true );
223        }
224        {
225            final Label label = new Label( "draftPoll", pollName );
226            y.add( label );
227            label.setRenderBodyOnly( true );
228        }
229
230      // Remote paste.
231      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
232        {
233            final IModel<String> href = new AbstractReadOnlyModel<String>()
234            {
235                public @Override String getObject()
236                {
237                    return remoteWikiIndexScript + "?action=edit&title=" + UriComponent.encode(
238                      remoteDraftName.getObject().toString(), UriComponent.Type.QUERY_PARAM );
239                }
240            };
241            y.add( new ExternalLink( "remotePasteLink", href, href ));
242        }
243    }
244
245
246
247//// P r i v a t e ///////////////////////////////////////////////////////////////////////
248
249
250    private static void addLocalLogin( final MarkupContainer y, final IDPair user,
251      final VoteServer vS )
252    {
253        final Label label = new Label( "userEmail", user.email() );
254        y.add( label );
255        label.setRenderBodyOnly( true );
256
257        final Model<String> href = new Model<String>( vS.pollwiki().appendPageSpecifier(
258          "Special:UserLogin" ).toString() );
259        y.add( new ExternalLink( "localLoginLink", href, href ));
260    }
261
262
263
264    private static ExternalLink newLocalPasteLink( final String coreLoc )
265    {
266        final Model<String> href = new Model<String>( coreLoc
267          + (coreLoc.lastIndexOf("?") < 0? "?": "&") + "action=edit" );
268        return new ExternalLink( "localPasteLink", href, href );
269    }
270
271
272
273}