@@ -35,8 +35,6 @@ public class EditorLockController {
35
35
36
36
37
37
private static String lockFeed (Request req , Response res ) {
38
- // FIXME: why is content type not being set in before()/after()?
39
- res .type ("application/json" );
40
38
Auth0UserProfile userProfile = req .attribute ("user" );
41
39
String feedId = req .queryParams ("feedId" );
42
40
EditorSession currentSession = sessionsForFeedIds .get (feedId );
@@ -95,13 +93,10 @@ private static String getLockedFeedMessage(EditorSession session, long minutesUn
95
93
private static String invalidateAndCreateNewSession (Request req ) {
96
94
req .session ().invalidate ();
97
95
Session session = req .session (true );
98
- String newSessionId = session .id ();
99
- return newSessionId ;
96
+ return session .id ();
100
97
}
101
98
102
99
private static String maintainLock (Request req , Response res ) {
103
- // FIXME: why is content type not being set in before()/after()?
104
- res .type ("application/json" );
105
100
String sessionId = req .params ("id" );
106
101
String feedId = req .queryParams ("feedId" );
107
102
Auth0UserProfile userProfile = req .attribute ("user" );
@@ -121,7 +116,7 @@ private static String maintainLock(Request req, Response res) {
121
116
if (currentSession .userEmail .equals (userProfile .getEmail ())) {
122
117
// If the new current session is held by this user, give them the option to evict the current session /
123
118
// unlock the feed.
124
- LOG .warn ("User {} already has an active editor session () for feed {}." , userProfile .getEmail (), currentSession .sessionId , currentSession .feedId );
119
+ LOG .warn ("User {} already has an active editor session {} for feed {}." , userProfile .getEmail (), currentSession .sessionId , currentSession .feedId );
125
120
logMessageAndHalt (req , 400 , "Warning! You have an active editing session for this feed underway in a different browser tab." );
126
121
} else {
127
122
LOG .warn ("User {} attempted editor session for feed {} while active session underway for user {}." , userProfile .getEmail (), currentSession .feedId , currentSession .userEmail );
@@ -132,15 +127,26 @@ private static String maintainLock(Request req, Response res) {
132
127
// Otherwise, the current session matches the session the user is attempting to maintain. Update the
133
128
// lastEdited time.
134
129
currentSession .lastCheckIn = System .currentTimeMillis ();
135
- // LOG.info("Updating session {} check-in time to {} for user {}", currentSession.sessionId, currentSession.lastCheckIn, currentSession.userEmail);
136
130
return formatJSON ("Updating time for user " + currentSession .userEmail , 200 , feedId , null );
137
131
}
138
132
}
139
133
134
+ /**
135
+ * Normal path for deleting a feed lock.
136
+ */
140
137
private static String deleteFeedLock (Request req , Response res ) {
141
- // FIXME: why is content type not being set in before()/after()?
142
- res .type ("application/json" );
143
- Auth0UserProfile userProfile = req .attribute ("user" );
138
+ return deleteFeedLockCore (req , req .attribute ("user" ));
139
+ }
140
+
141
+ /**
142
+ * Remove a feed lock when a browser calls sendBeacon() when closing/refreshing/navigating away from editor.
143
+ */
144
+ private static String deleteFeedLockBeacon (Request req , Response res ) {
145
+ // The sendBeacon call does not contain any Authorization headers, so we just pass a null userProfile.
146
+ return deleteFeedLockCore (req , null );
147
+ }
148
+
149
+ private static String deleteFeedLockCore (Request req , Auth0UserProfile userProfile ) {
144
150
String feedId = req .queryParams ("feedId" );
145
151
String sessionId = req .params ("id" );
146
152
EditorSession currentSession = sessionsForFeedIds .get (feedId );
@@ -153,8 +159,8 @@ private static String deleteFeedLock(Request req, Response res) {
153
159
// Note: There used to be a check here that the requesting user was the same as the user with an open
154
160
// session; however, this has been removed because in practice it became a nuisance. Respectful users with
155
161
// shared access to a feed can generally be trusted not to boot one another out in a combative manner.
156
- boolean overwrite = Boolean .valueOf (req .queryParams ("overwrite" ));
157
- if (overwrite ) {
162
+ boolean overwrite = Boolean .parseBoolean (req .queryParams ("overwrite" ));
163
+ if (userProfile != null && overwrite ) {
158
164
sessionId = invalidateAndCreateNewSession (req );
159
165
EditorSession newEditorSession = new EditorSession (feedId , sessionId , userProfile );
160
166
sessionsForFeedIds .put (feedId , newEditorSession );
@@ -165,7 +171,13 @@ private static String deleteFeedLock(Request req, Response res) {
165
171
return SparkUtils .formatJSON ("Not processing request to delete lock. There is already an active session for user " + currentSession .userEmail , 202 );
166
172
}
167
173
} else {
168
- LOG .info ("Current session: {} {}; User session: {} {}" , currentSession .userEmail , currentSession .sessionId , userProfile .getEmail (), sessionId );
174
+ LOG .info (
175
+ "Current session: {} {}; User session: {} {}" ,
176
+ currentSession .userEmail ,
177
+ currentSession .sessionId ,
178
+ userProfile != null ? userProfile .getEmail () : "(email unavailable)" ,
179
+ sessionId
180
+ );
169
181
// Otherwise, the current session matches the session from which the delete request came. This indicates that
170
182
// the user's editing session has been closed (by either exiting the editor or closing the browser tab).
171
183
LOG .info ("Closed session {} for feed {} successfully." , currentSession .sessionId , currentSession .feedId );
@@ -178,6 +190,9 @@ public static void register(String apiPrefix) {
178
190
post (apiPrefix + "secure/lock" , EditorLockController ::lockFeed , json ::write );
179
191
delete (apiPrefix + "secure/lock/:id" , EditorLockController ::deleteFeedLock , json ::write );
180
192
put (apiPrefix + "secure/lock/:id" , EditorLockController ::maintainLock , json ::write );
193
+ // Extra, unsecure POST method for removing lock via a browser's Navigator.sendBeacon() method.
194
+ // (Navigator.sendBeacon() sends a POST and does not support authorization headers.)
195
+ post (apiPrefix + "deletelock/:id" , EditorLockController ::deleteFeedLockBeacon , json ::write );
181
196
}
182
197
183
198
private static String formatJSON (String message , int code , String feedId , String sessionId ) {
0 commit comments