mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-02-11 00:00:07 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
467830e4de | ||
|
|
030c28eb93 | ||
|
|
e4702059c8 | ||
|
|
877813ed0f | ||
|
|
ccfb4f0b54 | ||
|
|
e27fd601a8 | ||
|
|
7c7f561f90 | ||
|
|
689cb5f26a | ||
|
|
1e4c508737 | ||
|
|
ef08ab5516 | ||
|
|
18ad23f503 | ||
|
|
d68687af62 | ||
|
|
0cdf441830 |
@@ -42,6 +42,11 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-move {
|
||||||
|
width: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.column-delete {
|
.column-delete {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -64,6 +69,16 @@
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#create-input {
|
||||||
|
width: 50%;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#move-input {
|
||||||
|
width: 80%;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Bootstrap overrides */
|
/* Bootstrap overrides */
|
||||||
|
|
||||||
.btn:focus {
|
.btn:focus {
|
||||||
|
|||||||
@@ -95,12 +95,54 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="create-modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Create Folder</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Please enter the name of the folder to be created:</p>
|
||||||
|
<form onsubmit="return false">
|
||||||
|
<input type="text" autocomplete="off" id="create-input">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="create-confirm">Create Folder</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="move-modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">Move Item</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Please enter the new location for this item:</p>
|
||||||
|
<form onsubmit="return false">
|
||||||
|
<input type="text" autocomplete="off" id="move-input">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="move-confirm">Move Item</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="text/x-tmpl" id="template-listing">
|
<script type="text/x-tmpl" id="template-listing">
|
||||||
<tr class="row-file" data-path="{%=o.path%}" data-name="{%=o.name%}">
|
<tr class="row-file" data-path="{%=o.path%}" data-name="{%=o.name%}">
|
||||||
<td class="column-icon">
|
<td class="column-icon">
|
||||||
{% if (o.size != null) { %}
|
{% if (o.size != null) { %}
|
||||||
<button type="button" class="btn btn-default btn-xs button-download">
|
<button type="button" class="btn btn-default btn-xs button-download">
|
||||||
<span class="glyphicon glyphicon-download"></span>
|
<span class="glyphicon glyphicon-download-alt"></span>
|
||||||
</button>
|
</button>
|
||||||
{% } else { %}
|
{% } else { %}
|
||||||
<button type="button" class="btn btn-default btn-xs button-open">
|
<button type="button" class="btn btn-default btn-xs button-open">
|
||||||
@@ -114,6 +156,11 @@
|
|||||||
<p>{%=formatFileSize(o.size)%}</p>
|
<p>{%=formatFileSize(o.size)%}</p>
|
||||||
{% } %}
|
{% } %}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="column-move">
|
||||||
|
<button type="button" class="btn btn-default btn-xs button-move">
|
||||||
|
<span class="glyphicon glyphicon glyphicon-share-alt"></span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
<td class="column-delete">
|
<td class="column-delete">
|
||||||
<button type="button" class="btn btn-danger btn-xs button-delete">
|
<button type="button" class="btn btn-danger btn-xs button-delete">
|
||||||
<span class="glyphicon glyphicon-trash"></span>
|
<span class="glyphicon glyphicon-trash"></span>
|
||||||
@@ -132,7 +179,7 @@
|
|||||||
<td class="column-path"><p>{%=o.path%}</p></td>
|
<td class="column-path"><p>{%=o.path%}</p></td>
|
||||||
<td class="column-progress">
|
<td class="column-progress">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" id="progress-bar"></div>
|
<div class="progress-bar" id="progress-bar"></div>
|
||||||
</div>
|
</div>
|
||||||
</ts>
|
</ts>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -140,7 +187,7 @@
|
|||||||
|
|
||||||
<script type="text/x-tmpl" id="template-alert">
|
<script type="text/x-tmpl" id="template-alert">
|
||||||
<div class="alert alert-{%=o.level%} alert-dismissable">
|
<div class="alert alert-{%=o.level%} alert-dismissable">
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||||
<strong>{%=o.title%}</strong>{%=o.description%}
|
<strong>{%=o.title%}</strong>{%=o.description%}
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -122,6 +122,16 @@ function _reload(path) {
|
|||||||
_reload(path);
|
_reload(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".button-move").click(function(event) {
|
||||||
|
var path = $(this).parent().parent().attr("data-path");
|
||||||
|
if (path[path.length - 1] == "/") {
|
||||||
|
path = path.slice(0, path.length - 1);
|
||||||
|
}
|
||||||
|
$("#move-input").attr("data-path", path);
|
||||||
|
$("#move-input").val(path);
|
||||||
|
$("#move-modal").modal("show");
|
||||||
|
});
|
||||||
|
|
||||||
$(".button-delete").click(function(event) {
|
$(".button-delete").click(function(event) {
|
||||||
var path = $(this).parent().parent().attr("data-path");
|
var path = $(this).parent().parent().attr("data-path");
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -202,9 +212,20 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#create-modal").on("shown.bs.modal", function(event) {
|
||||||
|
$("#create-input").focus();
|
||||||
|
$("#create-input").select();
|
||||||
|
})
|
||||||
|
|
||||||
$("#create-folder").click(function(event) {
|
$("#create-folder").click(function(event) {
|
||||||
var name = prompt("Please enter folder name:", "Untitled folder");
|
$("#create-input").val("Untitled folder");
|
||||||
if ((name != null) && (name != "")) {
|
$("#create-modal").modal("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#create-confirm").click(function(event) {
|
||||||
|
$("#create-modal").modal("hide");
|
||||||
|
var name = $("#create-input").val();
|
||||||
|
if (name != "") {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'create',
|
url: 'create',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
@@ -218,6 +239,29 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#move-modal").on("shown.bs.modal", function(event) {
|
||||||
|
$("#move-input").focus();
|
||||||
|
$("#move-input").select();
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#move-confirm").click(function(event) {
|
||||||
|
$("#move-modal").modal("hide");
|
||||||
|
var oldPath = $("#move-input").attr("data-path");
|
||||||
|
var newPath = $("#move-input").val();
|
||||||
|
if ((newPath != "") && (newPath[0] == "/") && (newPath != oldPath)) {
|
||||||
|
$.ajax({
|
||||||
|
url: 'move',
|
||||||
|
type: 'POST',
|
||||||
|
data: {oldPath: oldPath, newPath: newPath},
|
||||||
|
dataType: 'json'
|
||||||
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
|
_showError("Failed moving \"" + oldPath + "\" to \"" + newPath + "\"", textStatus, errorThrown);
|
||||||
|
}).always(function() {
|
||||||
|
_reload(_path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#reload").click(function(event) {
|
$("#reload").click(function(event) {
|
||||||
_reload(_path);
|
_reload(_path);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
@property(nonatomic, copy) NSString* title; // Default is application name (must be HTML escaped)
|
@property(nonatomic, copy) NSString* title; // Default is application name (must be HTML escaped)
|
||||||
@property(nonatomic, copy) NSString* header; // Default is same as title (must be HTML escaped)
|
@property(nonatomic, copy) NSString* header; // Default is same as title (must be HTML escaped)
|
||||||
@property(nonatomic, copy) NSString* prologue; // Default is mini help (must be raw HTML)
|
@property(nonatomic, copy) NSString* prologue; // Default is mini help (must be raw HTML)
|
||||||
@property(nonatomic, copy) NSString* epilogue; // Default is mini help (must be raw HTML)
|
@property(nonatomic, copy) NSString* epilogue; // Default is nothing (must be raw HTML)
|
||||||
@property(nonatomic, copy) NSString* footer; // Default is application name and version (must be HTML escaped)
|
@property(nonatomic, copy) NSString* footer; // Default is application name and version (must be HTML escaped)
|
||||||
- (id)initWithUploadDirectory:(NSString*)path;
|
- (id)initWithUploadDirectory:(NSString*)path;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -88,7 +88,11 @@
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||||
GCDWebUploader* __unsafe_unretained uploader = self; // Avoid retain-cycles with self
|
#if __has_feature(objc_arc)
|
||||||
|
__unsafe_unretained GCDWebUploader* uploader = self;
|
||||||
|
#else
|
||||||
|
__block GCDWebUploader* uploader = self;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Resource files
|
// Resource files
|
||||||
[self addGETHandlerForBasePath:@"/" directoryPath:[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO];
|
[self addGETHandlerForBasePath:@"/" directoryPath:[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO];
|
||||||
@@ -108,6 +112,11 @@
|
|||||||
NSString* title = uploader.title;
|
NSString* title = uploader.title;
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
if (title == nil) {
|
||||||
|
title = [[NSProcessInfo processInfo] processName];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
NSString* header = uploader.header;
|
NSString* header = uploader.header;
|
||||||
if (header == nil) {
|
if (header == nil) {
|
||||||
@@ -123,9 +132,15 @@
|
|||||||
}
|
}
|
||||||
NSString* footer = uploader.footer;
|
NSString* footer = uploader.footer;
|
||||||
if (footer == nil) {
|
if (footer == nil) {
|
||||||
footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil],
|
NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],
|
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];
|
#if !TARGET_OS_IPHONE
|
||||||
|
if (!name && !version) {
|
||||||
|
name = @"OS X";
|
||||||
|
version = [[NSProcessInfo processInfo] operatingSystemVersionString];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil], name, version];
|
||||||
}
|
}
|
||||||
return [GCDWebServerDataResponse responseWithHTMLTemplate:[siteBundle pathForResource:@"index" ofType:@"html"]
|
return [GCDWebServerDataResponse responseWithHTMLTemplate:[siteBundle pathForResource:@"index" ofType:@"html"]
|
||||||
variables:@{
|
variables:@{
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ GCDWebServer is a lightweight GCD based HTTP 1.1 server designed to be embedded
|
|||||||
* No dependencies on third-party source code
|
* No dependencies on third-party source code
|
||||||
* Available under a friendly [New BSD License](LICENSE)
|
* Available under a friendly [New BSD License](LICENSE)
|
||||||
|
|
||||||
|
Included extensions:
|
||||||
|
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of GCDWebServer that implements an interface for uploading and downloading files from an iOS app's sandbox using a web browser
|
||||||
|
|
||||||
What's not available out of the box but can be implemented on top of the API:
|
What's not available out of the box but can be implemented on top of the API:
|
||||||
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||||
* Web forms submitted using "multipart/mixed"
|
* Web forms submitted using "multipart/mixed"
|
||||||
@@ -70,6 +73,7 @@ Simply instantiate and run a GCDWebUploader instance then visit http://{YOUR-IOS
|
|||||||
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||||
GCDWebUploader* webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
GCDWebUploader* webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
||||||
[webUploader start];
|
[webUploader start];
|
||||||
|
NSLog(@"Visit %@ in your web browser", webUploader.serverURL);
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -222,4 +226,4 @@ Final Example: File Downloads and Uploads From iOS App
|
|||||||
|
|
||||||
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It uses it to provide a web server for people to upload and download comic files directly over WiFi to and from the app.
|
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It uses it to provide a web server for people to upload and download comic files directly over WiFi to and from the app.
|
||||||
|
|
||||||
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebServer in the [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) file.
|
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebUploader in the [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) file.
|
||||||
|
|||||||
Reference in New Issue
Block a user