Revision a2f617f8
b/src/gr/grnet/pithos/web/client/FileList.java | ||
---|---|---|
60 | 60 |
import com.google.gwt.safehtml.shared.SafeHtmlBuilder; |
61 | 61 |
import com.google.gwt.user.cellview.client.CellTable; |
62 | 62 |
import com.google.gwt.user.cellview.client.Column; |
63 |
import com.google.gwt.user.client.Command; |
|
63 | 64 |
import com.google.gwt.user.client.DOM; |
64 | 65 |
import com.google.gwt.user.client.Event; |
65 | 66 |
import com.google.gwt.user.client.Window; |
... | ... | |
318 | 319 |
vp.setCellWidth(celltable, "100%"); |
319 | 320 |
vp.addHandler(new ContextMenuHandler() { |
320 | 321 |
@Override |
321 |
public void onContextMenu(ContextMenuEvent event) { |
|
322 |
TreeView tree = app.getSelectedTree(); |
|
322 |
public void onContextMenu(final ContextMenuEvent event) {
|
|
323 |
final TreeView tree = app.getSelectedTree();
|
|
323 | 324 |
if (tree != null && (tree.equals(app.getFolderTreeView()) || tree.equals(app.getOtherSharedTreeView()))) { |
324 |
Folder selectedFolder = app.getSelection(); |
|
325 |
FileContextMenu contextMenu = new FileContextMenu(app, images, tree, selectedFolder, getSelectedFiles()); |
|
326 |
int x = event.getNativeEvent().getClientX(); |
|
327 |
int y = event.getNativeEvent().getClientY(); |
|
328 |
contextMenu.setPopupPosition(x, y); |
|
329 |
contextMenu.show(); |
|
325 |
final int x = event.getNativeEvent().getClientX(); |
|
326 |
final int y = event.getNativeEvent().getClientY(); |
|
327 |
final Folder selectedFolder = app.getSelection(); |
|
328 |
app.scheduleFolderHeadCommand(selectedFolder, new Command() { |
|
329 |
|
|
330 |
@Override |
|
331 |
public void execute() { |
|
332 |
final List<File> selectedFiles = getSelectedFiles(); |
|
333 |
Iterator<File> iter = selectedFiles.iterator(); |
|
334 |
iterateFilesHeadCommand(iter, new Command() { |
|
335 |
|
|
336 |
@Override |
|
337 |
public void execute() { |
|
338 |
FileContextMenu contextMenu = new FileContextMenu(app, images, tree, selectedFolder, selectedFiles); |
|
339 |
contextMenu.setPopupPosition(x, y); |
|
340 |
contextMenu.show(); |
|
341 |
} |
|
342 |
}); |
|
343 |
} |
|
344 |
}); |
|
330 | 345 |
} |
331 | 346 |
} |
332 | 347 |
}, ContextMenuEvent.getType()); |
... | ... | |
589 | 604 |
//celltable.redraw(); |
590 | 605 |
celltable.redrawHeaders(); |
591 | 606 |
} |
607 |
|
|
608 |
void iterateFilesHeadCommand(final Iterator<File> iter, final Command callback) { |
|
609 |
if (iter.hasNext()) { |
|
610 |
File f = iter.next(); |
|
611 |
app.scheduleFileHeadCommand(f, new Command() { |
|
612 |
|
|
613 |
@Override |
|
614 |
public void execute() { |
|
615 |
iterateFilesHeadCommand(iter, callback); |
|
616 |
} |
|
617 |
}); |
|
618 |
} |
|
619 |
else if (callback != null) |
|
620 |
callback.execute(); |
|
621 |
} |
|
592 | 622 |
} |
b/src/gr/grnet/pithos/web/client/Pithos.java | ||
---|---|---|
1188 | 1188 |
upload.addStyleName("pithos-uploadButton-loading"); |
1189 | 1189 |
upload.setTitle("Upload in progress. Click for details."); |
1190 | 1190 |
} |
1191 |
|
|
1192 |
public void scheduleFolderHeadCommand(final Folder folder, final Command callback) { |
|
1193 |
HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getApiPath(), folder.getOwner(), folder.getUri(), folder) { |
|
1194 |
|
|
1195 |
@Override |
|
1196 |
public void onSuccess(Folder _result) { |
|
1197 |
if (callback != null) |
|
1198 |
callback.execute(); |
|
1199 |
} |
|
1200 |
|
|
1201 |
@Override |
|
1202 |
public void onError(Throwable t) { |
|
1203 |
if (t instanceof RestException) { |
|
1204 |
if (((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) { |
|
1205 |
final String path = folder.getUri(); |
|
1206 |
PutRequest newFolder = new PutRequest(getApiPath(), folder.getOwner(), path) { |
|
1207 |
@Override |
|
1208 |
public void onSuccess(Resource _result) { |
|
1209 |
scheduleFolderHeadCommand(folder, callback); |
|
1210 |
} |
|
1211 |
|
|
1212 |
@Override |
|
1213 |
public void onError(Throwable _t) { |
|
1214 |
GWT.log("", _t); |
|
1215 |
setError(_t); |
|
1216 |
if(_t instanceof RestException){ |
|
1217 |
displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText()); |
|
1218 |
} |
|
1219 |
else |
|
1220 |
displayError("System error creating folder: " + _t.getMessage()); |
|
1221 |
} |
|
1222 |
|
|
1223 |
@Override |
|
1224 |
protected void onUnauthorized(Response response) { |
|
1225 |
sessionExpired(); |
|
1226 |
} |
|
1227 |
}; |
|
1228 |
newFolder.setHeader("X-Auth-Token", getToken()); |
|
1229 |
newFolder.setHeader("Content-Type", "application/folder"); |
|
1230 |
newFolder.setHeader("Accept", "*/*"); |
|
1231 |
newFolder.setHeader("Content-Length", "0"); |
|
1232 |
Scheduler.get().scheduleDeferred(newFolder); |
|
1233 |
} |
|
1234 |
else |
|
1235 |
displayError("Error heading folder: " + ((RestException) t).getHttpStatusText()); |
|
1236 |
} |
|
1237 |
else |
|
1238 |
displayError("System error heading folder: " + t.getMessage()); |
|
1239 |
|
|
1240 |
GWT.log("Error heading folder", t); |
|
1241 |
setError(t); |
|
1242 |
} |
|
1243 |
|
|
1244 |
@Override |
|
1245 |
protected void onUnauthorized(Response response) { |
|
1246 |
sessionExpired(); |
|
1247 |
} |
|
1248 |
}; |
|
1249 |
headFolder.setHeader("X-Auth-Token", getToken()); |
|
1250 |
Scheduler.get().scheduleDeferred(headFolder); |
|
1251 |
} |
|
1252 |
|
|
1253 |
public void scheduleFileHeadCommand(File f, final Command callback) { |
|
1254 |
HeadRequest<File> headFile = new HeadRequest<File>(File.class, getApiPath(), f.getOwner(), f.getUri(), f) { |
|
1255 |
|
|
1256 |
@Override |
|
1257 |
public void onSuccess(File _result) { |
|
1258 |
if (callback != null) |
|
1259 |
callback.execute(); |
|
1260 |
} |
|
1261 |
|
|
1262 |
@Override |
|
1263 |
public void onError(Throwable t) { |
|
1264 |
GWT.log("Error heading file", t); |
|
1265 |
setError(t); |
|
1266 |
if (t instanceof RestException) |
|
1267 |
displayError("Error heading file: " + ((RestException) t).getHttpStatusText()); |
|
1268 |
else |
|
1269 |
displayError("System error heading file: " + t.getMessage()); |
|
1270 |
} |
|
1271 |
|
|
1272 |
@Override |
|
1273 |
protected void onUnauthorized(Response response) { |
|
1274 |
sessionExpired(); |
|
1275 |
} |
|
1276 |
}; |
|
1277 |
headFile.setHeader("X-Auth-Token", getToken()); |
|
1278 |
Scheduler.get().scheduleDeferred(headFile); |
|
1279 |
} |
|
1191 | 1280 |
} |
b/src/gr/grnet/pithos/web/client/Toolbar.java | ||
---|---|---|
51 | 51 |
|
52 | 52 |
Pithos app; |
53 | 53 |
|
54 |
private Anchor newFolderButton;
|
|
54 |
Anchor newFolderButton; |
|
55 | 55 |
|
56 |
private Anchor shareFolderButton;
|
|
56 |
Anchor shareFolderButton; |
|
57 | 57 |
|
58 |
private Anchor refreshButton;
|
|
58 |
Anchor refreshButton; |
|
59 | 59 |
|
60 | 60 |
private Anchor toolsButton; |
61 | 61 |
|
... | ... | |
153 | 153 |
} |
154 | 154 |
|
155 | 155 |
public void showRelevantButtons() { |
156 |
Folder folder = app.getSelectedTree().getSelection(); |
|
156 |
final Folder folder = app.getSelectedTree().getSelection();
|
|
157 | 157 |
if (folder != null) { |
158 |
Boolean[] permissions = folder.getPermissions().get(app.getUsername()); |
|
159 |
boolean canWrite = folder.getOwner().equals(app.getUsername()) || (permissions!= null && permissions[1] != null && permissions[1]); |
|
160 |
boolean isFolderTreeSelected = app.getSelectedTree().equals(app.getFolderTreeView()); |
|
161 |
boolean otherSharedTreeSelected = app.getSelectedTree().equals(app.getOtherSharedTreeView()); |
|
162 |
|
|
163 |
if (isFolderTreeSelected || otherSharedTreeSelected) |
|
164 |
refreshButton.setVisible(true); |
|
165 |
else |
|
166 |
refreshButton.setVisible(true); |
|
167 |
|
|
168 |
if (!folder.isInTrash() && canWrite) { |
|
169 |
if (isFolderTreeSelected || otherSharedTreeSelected) |
|
170 |
newFolderButton.setVisible(true); |
|
171 |
if (isFolderTreeSelected && !folder.isContainer()) |
|
172 |
shareFolderButton.setVisible(true); |
|
173 |
else |
|
174 |
shareFolderButton.setVisible(false); |
|
175 |
} |
|
176 |
else { |
|
177 |
newFolderButton.setVisible(false); |
|
178 |
shareFolderButton.setVisible(false); |
|
179 |
} |
|
158 |
app.scheduleFolderHeadCommand(folder, new Command() { |
|
159 |
|
|
160 |
@Override |
|
161 |
public void execute() { |
|
162 |
Boolean[] permissions = folder.getPermissions().get(app.getUsername()); |
|
163 |
boolean canWrite = folder.getOwner().equals(app.getUsername()) || (permissions!= null && permissions[1] != null && permissions[1]); |
|
164 |
boolean isFolderTreeSelected = app.getSelectedTree().equals(app.getFolderTreeView()); |
|
165 |
boolean otherSharedTreeSelected = app.getSelectedTree().equals(app.getOtherSharedTreeView()); |
|
166 |
|
|
167 |
if (isFolderTreeSelected || otherSharedTreeSelected) |
|
168 |
refreshButton.setVisible(true); |
|
169 |
else |
|
170 |
refreshButton.setVisible(true); |
|
171 |
|
|
172 |
if (!folder.isInTrash() && canWrite) { |
|
173 |
if (isFolderTreeSelected || otherSharedTreeSelected) |
|
174 |
newFolderButton.setVisible(true); |
|
175 |
if (isFolderTreeSelected && !folder.isContainer()) |
|
176 |
shareFolderButton.setVisible(true); |
|
177 |
else |
|
178 |
shareFolderButton.setVisible(false); |
|
179 |
} |
|
180 |
else { |
|
181 |
newFolderButton.setVisible(false); |
|
182 |
shareFolderButton.setVisible(false); |
|
183 |
} |
|
184 |
} |
|
185 |
}); |
|
180 | 186 |
} |
181 | 187 |
else { |
182 | 188 |
newFolderButton.setVisible(false); |
b/src/gr/grnet/pithos/web/client/commands/PropertiesCommand.java | ||
---|---|---|
43 | 43 |
import gr.grnet.pithos.web.client.Pithos; |
44 | 44 |
import gr.grnet.pithos.web.client.foldertree.File; |
45 | 45 |
import gr.grnet.pithos.web.client.foldertree.Folder; |
46 |
import gr.grnet.pithos.web.client.foldertree.Resource; |
|
47 |
import gr.grnet.pithos.web.client.rest.HeadRequest; |
|
48 |
import gr.grnet.pithos.web.client.rest.PutRequest; |
|
49 |
import gr.grnet.pithos.web.client.rest.RestException; |
|
50 | 46 |
|
51 | 47 |
import java.util.List; |
52 | 48 |
|
53 |
import com.google.gwt.core.client.GWT; |
|
54 |
import com.google.gwt.core.client.Scheduler; |
|
55 |
import com.google.gwt.http.client.Response; |
|
56 | 49 |
import com.google.gwt.user.client.Command; |
57 | 50 |
import com.google.gwt.user.client.ui.PopupPanel; |
58 | 51 |
|
... | ... | |
99 | 92 |
dlg.center(); |
100 | 93 |
break; |
101 | 94 |
case PERMISSIONS: |
102 |
scheduleFolderHeadCommand(folder, new Command() { |
|
95 |
app.scheduleFolderHeadCommand(folder, new Command() {
|
|
103 | 96 |
|
104 | 97 |
@Override |
105 | 98 |
public void execute() { |
... | ... | |
123 | 116 |
final File f = files.get(0); |
124 | 117 |
switch (tabToShow) { |
125 | 118 |
case PROPERTIES: |
126 |
scheduleFileHeadCommand(f, new Command() { |
|
119 |
app.scheduleFileHeadCommand(f, new Command() {
|
|
127 | 120 |
|
128 | 121 |
@Override |
129 | 122 |
public void execute() { |
... | ... | |
133 | 126 |
}); |
134 | 127 |
break; |
135 | 128 |
case PERMISSIONS: |
136 |
scheduleFileHeadCommand(f, new Command() { |
|
129 |
app.scheduleFileHeadCommand(f, new Command() {
|
|
137 | 130 |
|
138 | 131 |
@Override |
139 | 132 |
public void execute() { |
... | ... | |
152 | 145 |
} |
153 | 146 |
} |
154 | 147 |
} |
155 |
|
|
156 |
private void scheduleFileHeadCommand(File f, final Command callback) { |
|
157 |
HeadRequest<File> headFile = new HeadRequest<File>(File.class, app.getApiPath(), f.getOwner(), f.getUri(), f) { |
|
158 |
|
|
159 |
@Override |
|
160 |
public void onSuccess(File _result) { |
|
161 |
if (callback != null) |
|
162 |
callback.execute(); |
|
163 |
} |
|
164 |
|
|
165 |
@Override |
|
166 |
public void onError(Throwable t) { |
|
167 |
GWT.log("Error heading file", t); |
|
168 |
app.setError(t); |
|
169 |
if (t instanceof RestException) |
|
170 |
app.displayError("Error heading file: " + ((RestException) t).getHttpStatusText()); |
|
171 |
else |
|
172 |
app.displayError("System error heading file: " + t.getMessage()); |
|
173 |
} |
|
174 |
|
|
175 |
@Override |
|
176 |
protected void onUnauthorized(Response response) { |
|
177 |
app.sessionExpired(); |
|
178 |
} |
|
179 |
}; |
|
180 |
headFile.setHeader("X-Auth-Token", app.getToken()); |
|
181 |
Scheduler.get().scheduleDeferred(headFile); |
|
182 |
} |
|
183 |
|
|
184 |
void scheduleFolderHeadCommand(final Folder folder, final Command callback) { |
|
185 |
HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, app.getApiPath(), folder.getOwner(), folder.getUri(), folder) { |
|
186 |
|
|
187 |
@Override |
|
188 |
public void onSuccess(Folder _result) { |
|
189 |
if (callback != null) |
|
190 |
callback.execute(); |
|
191 |
} |
|
192 |
|
|
193 |
@Override |
|
194 |
public void onError(Throwable t) { |
|
195 |
if (t instanceof RestException) { |
|
196 |
if (((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) { |
|
197 |
final String path = folder.getUri(); |
|
198 |
PutRequest newFolder = new PutRequest(app.getApiPath(), folder.getOwner(), path) { |
|
199 |
@Override |
|
200 |
public void onSuccess(Resource _result) { |
|
201 |
scheduleFolderHeadCommand(folder, callback); |
|
202 |
} |
|
203 |
|
|
204 |
@Override |
|
205 |
public void onError(Throwable _t) { |
|
206 |
GWT.log("", _t); |
|
207 |
app.setError(_t); |
|
208 |
if(_t instanceof RestException){ |
|
209 |
app.displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText()); |
|
210 |
} |
|
211 |
else |
|
212 |
app.displayError("System error creating folder: " + _t.getMessage()); |
|
213 |
} |
|
214 |
|
|
215 |
@Override |
|
216 |
protected void onUnauthorized(Response response) { |
|
217 |
app.sessionExpired(); |
|
218 |
} |
|
219 |
}; |
|
220 |
newFolder.setHeader("X-Auth-Token", app.getToken()); |
|
221 |
newFolder.setHeader("Content-Type", "application/folder"); |
|
222 |
newFolder.setHeader("Accept", "*/*"); |
|
223 |
newFolder.setHeader("Content-Length", "0"); |
|
224 |
Scheduler.get().scheduleDeferred(newFolder); |
|
225 |
} |
|
226 |
else |
|
227 |
app.displayError("Error heading folder: " + ((RestException) t).getHttpStatusText()); |
|
228 |
} |
|
229 |
else |
|
230 |
app.displayError("System error heading folder: " + t.getMessage()); |
|
231 |
|
|
232 |
GWT.log("Error heading folder", t); |
|
233 |
app.setError(t); |
|
234 |
} |
|
235 |
|
|
236 |
@Override |
|
237 |
protected void onUnauthorized(Response response) { |
|
238 |
app.sessionExpired(); |
|
239 |
} |
|
240 |
}; |
|
241 |
headFolder.setHeader("X-Auth-Token", app.getToken()); |
|
242 |
Scheduler.get().scheduleDeferred(headFolder); |
|
243 |
} |
|
244 | 148 |
} |
b/src/gr/grnet/pithos/web/client/foldertree/FolderTreeViewModel.java | ||
---|---|---|
88 | 88 |
} |
89 | 89 |
|
90 | 90 |
@Override |
91 |
public void onBrowserEvent(Cell.Context context, com.google.gwt.dom.client.Element parent, Folder folder, com.google.gwt.dom.client.NativeEvent event, com.google.gwt.cell.client.ValueUpdater<Folder> valueUpdater) { |
|
91 |
public void onBrowserEvent(Cell.Context context, com.google.gwt.dom.client.Element parent, final Folder folder, com.google.gwt.dom.client.NativeEvent event, com.google.gwt.cell.client.ValueUpdater<Folder> valueUpdater) {
|
|
92 | 92 |
if (event.getType().equals(ContextMenuEvent.getType().getName())) { |
93 |
final int x = event.getClientX(); |
|
94 |
final int y = event.getClientY(); |
|
93 | 95 |
FolderTreeViewModel.this.selectionModel.setSelected(folder, true); |
94 |
FolderContextMenu menu = new FolderContextMenu(app, FolderTreeView.images, app.getSelectedTree(), folder); |
|
95 |
menu.setPopupPosition(event.getClientX(), event.getClientY()); |
|
96 |
menu.show(); |
|
96 |
app.scheduleFolderHeadCommand(folder, new Command() { |
|
97 |
|
|
98 |
@Override |
|
99 |
public void execute() { |
|
100 |
FolderContextMenu menu = new FolderContextMenu(app, FolderTreeView.images, app.getSelectedTree(), folder); |
|
101 |
menu.setPopupPosition(x, y); |
|
102 |
menu.show(); |
|
103 |
} |
|
104 |
}); |
|
97 | 105 |
} |
98 | 106 |
} |
99 | 107 |
}; |
b/src/gr/grnet/pithos/web/client/mysharedtree/MysharedTreeViewModel.java | ||
---|---|---|
39 | 39 |
import gr.grnet.pithos.web.client.Pithos; |
40 | 40 |
import gr.grnet.pithos.web.client.foldertree.File; |
41 | 41 |
import gr.grnet.pithos.web.client.foldertree.Folder; |
42 |
import gr.grnet.pithos.web.client.foldertree.FolderTreeView; |
|
42 | 43 |
import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView.Templates; |
43 | 44 |
import gr.grnet.pithos.web.client.rest.GetRequest; |
44 | 45 |
import gr.grnet.pithos.web.client.rest.RestException; |
... | ... | |
85 | 86 |
} |
86 | 87 |
|
87 | 88 |
@Override |
88 |
public void onBrowserEvent(Context context, com.google.gwt.dom.client.Element parent, Folder folder, com.google.gwt.dom.client.NativeEvent event, ValueUpdater<Folder> valueUpdater) { |
|
89 |
public void onBrowserEvent(Context context, com.google.gwt.dom.client.Element parent, final Folder folder, com.google.gwt.dom.client.NativeEvent event, ValueUpdater<Folder> valueUpdater) {
|
|
89 | 90 |
if (event.getType().equals(ContextMenuEvent.getType().getName())) { |
91 |
final int x = event.getClientX(); |
|
92 |
final int y = event.getClientY(); |
|
90 | 93 |
MysharedTreeViewModel.this.selectionModel.setSelected(folder, true); |
91 |
FolderContextMenu menu = new FolderContextMenu(app, MysharedTreeView.images, app.getSelectedTree(), folder); |
|
92 |
menu.setPopupPosition(event.getClientX(), event.getClientY()); |
|
93 |
menu.show(); |
|
94 |
app.scheduleFolderHeadCommand(folder, new Command() { |
|
95 |
|
|
96 |
@Override |
|
97 |
public void execute() { |
|
98 |
FolderContextMenu menu = new FolderContextMenu(app, MysharedTreeView.images, app.getSelectedTree(), folder); |
|
99 |
menu.setPopupPosition(x, y); |
|
100 |
menu.show(); |
|
101 |
} |
|
102 |
}); |
|
94 | 103 |
} |
95 | 104 |
} |
96 | 105 |
}; |
b/src/gr/grnet/pithos/web/client/othersharedtree/OtherSharedTreeViewModel.java | ||
---|---|---|
41 | 41 |
import gr.grnet.pithos.web.client.foldertree.AccountResource; |
42 | 42 |
import gr.grnet.pithos.web.client.foldertree.File; |
43 | 43 |
import gr.grnet.pithos.web.client.foldertree.Folder; |
44 |
import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView; |
|
44 | 45 |
import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView.Templates; |
45 | 46 |
import gr.grnet.pithos.web.client.rest.GetRequest; |
46 | 47 |
import gr.grnet.pithos.web.client.rest.RestException; |
... | ... | |
86 | 87 |
} |
87 | 88 |
|
88 | 89 |
@Override |
89 |
public void onBrowserEvent(Context context, com.google.gwt.dom.client.Element parent, Folder folder, com.google.gwt.dom.client.NativeEvent event, ValueUpdater<Folder> valueUpdater) { |
|
90 |
public void onBrowserEvent(Context context, com.google.gwt.dom.client.Element parent, final Folder folder, com.google.gwt.dom.client.NativeEvent event, ValueUpdater<Folder> valueUpdater) {
|
|
90 | 91 |
if (event.getType().equals(ContextMenuEvent.getType().getName())) { |
92 |
final int x = event.getClientX(); |
|
93 |
final int y = event.getClientY(); |
|
91 | 94 |
OtherSharedTreeViewModel.this.selectionModel.setSelected(folder, true); |
92 |
FolderContextMenu menu = new FolderContextMenu(app, OtherSharedTreeView.images, app.getSelectedTree(), folder); |
|
93 |
menu.setPopupPosition(event.getClientX(), event.getClientY()); |
|
94 |
menu.show(); |
|
95 |
app.scheduleFolderHeadCommand(folder, new Command() { |
|
96 |
|
|
97 |
@Override |
|
98 |
public void execute() { |
|
99 |
FolderContextMenu menu = new FolderContextMenu(app, OtherSharedTreeView.images, app.getSelectedTree(), folder); |
|
100 |
menu.setPopupPosition(x, y); |
|
101 |
menu.show(); |
|
102 |
} |
|
103 |
}); |
|
95 | 104 |
} |
96 | 105 |
} |
97 | 106 |
}; |
Also available in: Unified diff