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}