Skip to content

Commit c075608

Browse files
committed
Initial commit
0 parents  commit c075608

File tree

7 files changed

+292
-0
lines changed

7 files changed

+292
-0
lines changed

Diff for: .meteor/packages

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Meteor packages used by this project, one per line.
2+
#
3+
# 'meteor add' and 'meteor remove' will edit this file for you,
4+
# but you can also edit it by hand.
5+
6+
standard-app-packages
7+
coffeescript
8+
bootstrap-3
9+
accounts-ui
10+
accounts-password
11+
fileCollection
12+
jobCollection
13+
jquery-cookie
14+
numeral

Diff for: README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## fileCollection + jobCollection Sample App
2+
3+
This demo app uses fileCollection's built-in support for [Resumable.js](http://www.resumablejs.com/) to allow drag and drop uploading of files. It also uses jobCollection to manage creation of thumbnail images for each uploaded file. Beyond that, it presents a simple image management grid with basic metadata, user acounts with file ownership, previews of images with click to load, and the ability to download or delete files.
4+
5+
To set-up, make sure you have [meteorite](https://atmospherejs.com/docs/installing) installed.
6+
7+
Then just run `mrt` in this directory and then once the app server is running, point your browser at `http://localhost:3000/`.

Diff for: public/pattern_130.gif

3.85 KB
Loading

Diff for: sample.coffee

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
############################################################################
2+
# Copyright (C) 2014 by Vaughn Iverson
3+
# fileCollection is free software released under the MIT/X11 license.
4+
# See included LICENSE file for details.
5+
############################################################################
6+
7+
# Both client and server
8+
9+
# Default collection name is 'fs'
10+
myData = FileCollection({
11+
resumable: true, # Enable the resumable.js compatible chunked file upload interface
12+
http: [ { method: 'get', path: '/:md5', lookup: (params, query) -> return { md5: params.md5 }}]}
13+
# Define a GET API that uses the md5 sum id files
14+
)
15+
16+
############################################################
17+
# Client-only code
18+
############################################################
19+
20+
if Meteor.isClient
21+
22+
Meteor.subscribe 'allData'
23+
24+
Meteor.startup () ->
25+
26+
# Set up an autorun to keep the X-Auth-Token cookie up-to-date
27+
Deps.autorun () ->
28+
Meteor.userId()
29+
token = Accounts._storedLoginToken()
30+
$.cookie 'X-Auth-Token', token
31+
32+
################################
33+
# Setup resumable.js in the UI
34+
35+
# This assigns a file drop zone to the "file table"
36+
myData.resumable.assignDrop $(".fileDrop")
37+
38+
# When a file is added
39+
myData.resumable.on 'fileAdded', (file) ->
40+
# Keep track of its progress reactivaly in a session variable
41+
Session.set file.uniqueIdentifier, 0
42+
# Create a new file in the file collection to upload to
43+
myData.insert({
44+
_id: file.uniqueIdentifier # This is the ID resumable will use
45+
filename: file.fileName
46+
contentType: file.file.type
47+
},
48+
(err, _id) ->
49+
if err
50+
console.warn "File creation failed!", err
51+
return
52+
# Once the file exists on the server, start uploading
53+
myData.resumable.upload()
54+
)
55+
56+
# Update the upload progress session variable
57+
myData.resumable.on 'fileProgress', (file) ->
58+
Session.set file.uniqueIdentifier, Math.floor(100*file.progress())
59+
60+
# Finish the upload progress in the session variable
61+
myData.resumable.on 'fileSuccess', (file) ->
62+
Session.set file.uniqueIdentifier, undefined
63+
64+
# More robust error handling needed!
65+
myData.resumable.on 'fileError', (file) ->
66+
console.warn "Error uploading", file.uniqueIdentifier
67+
Session.set file.uniqueIdentifier, undefined
68+
69+
#####################
70+
# UI template helpers
71+
72+
Template.collTest.events
73+
# Wire up the event to remove a file by clicking the `X`
74+
'click .del-file': (e, t) ->
75+
# Just the remove method does it all
76+
myData.remove {_id: this._id}
77+
78+
Template.collTest.dataEntries = () ->
79+
# Reactively populate the table
80+
myData.find({})
81+
82+
Template.collTest.owner = () ->
83+
this.metadata?._auth?.owner
84+
85+
Template.collTest.id = () ->
86+
"#{this._id}"
87+
88+
Template.collTest.link = () ->
89+
myData.baseURL + "/" + this.md5
90+
91+
Template.collTest.uploadStatus = () ->
92+
percent = Session.get "#{this._id}"
93+
unless percent?
94+
"Processing..."
95+
else
96+
"Uploading..."
97+
98+
Template.collTest.formattedLength = () ->
99+
numeral(this.length).format('0.0b')
100+
101+
Template.collTest.uploadProgress = () ->
102+
percent = Session.get "#{this._id}"
103+
104+
Template.collTest.isImage = () ->
105+
types =
106+
'image/jpeg': true
107+
'image/png': true
108+
'image/gif': true
109+
'image/tiff': true
110+
types[this.contentType]?
111+
112+
Template.collTest.loginToken = () ->
113+
Meteor.userId()
114+
Accounts._storedLoginToken()
115+
116+
Template.collTest.userId = () ->
117+
Meteor.userId()
118+
119+
############################################################
120+
# Server-only code
121+
############################################################
122+
123+
if Meteor.isServer
124+
125+
Meteor.startup () ->
126+
127+
# Only publish files owned by this userId, and ignore temp file chunks used by resumable
128+
Meteor.publish 'allData', () ->
129+
myData.find({ 'metadata._Resumable': { $exists: false }, 'metadata._auth.owner': this.userId })
130+
131+
# Don't allow users to modify the user docs
132+
Meteor.users.deny({update: () -> true })
133+
134+
# Allow rules for security. Without these, no writes would be allowed by default
135+
myData.allow
136+
insert: (userId, file) ->
137+
# Assign the proper owner when a file is created
138+
file.metadata = file.metadata ? {}
139+
file.metadata._auth =
140+
owner: userId
141+
true
142+
remove: (userId, file) ->
143+
# Only owners can delete
144+
if file.metadata?._auth?.owner and userId isnt file.metadata._auth.owner
145+
return false
146+
true
147+
read: (userId, file) ->
148+
# Only owners can GET file data
149+
if file.metadata?._auth?.owner and userId isnt file.metadata._auth.owner
150+
return false
151+
true
152+
write: (userId, file, fields) -> # This is for the HTTP REST interfaces PUT/POST
153+
# All client file metadata updates are denied, implement Methods for that...
154+
# Only owners can upload a file
155+
if file.metadata?._auth?.owner and userId isnt file.metadata._auth.owner
156+
return false
157+
true

Diff for: sample.css

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* CSS declarations go here */
2+
3+
body {
4+
background: url('pattern_130.gif');
5+
}
6+
7+
.panel {
8+
position: relative;
9+
background: #fff;
10+
margin-top: 10px;
11+
box-shadow: 0 0 10px rgba(0,0,0,.5);
12+
}
13+
14+
.content {
15+
/*padding: 10px;*/
16+
}
17+
18+
.loginTemp {
19+
position: absolute;
20+
top: 20px;
21+
right: 20px;
22+
}

Diff for: sample.html

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<head>
2+
<meta charset="utf-8">
3+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
4+
<title>Upload test</title>
5+
<meta name="description" content="">
6+
<meta name="viewport" content="width=device-width">
7+
</head>
8+
<body>
9+
<div id="fileDropID">
10+
{{> collTest}}
11+
</div>
12+
</body>
13+
14+
<template name="collTest">
15+
<div class="container panel">
16+
<h3>fileCollection Test App</h3>
17+
<span class="pull-right text-info">UserId: {{userId}} Token: {{loginToken}}</span>
18+
<div class="loginTemp">
19+
{{> loginButtons align="right"}}
20+
</div>
21+
</div>
22+
<div class="container panel fileDrop">
23+
<table class="table table-bordered table-striped">
24+
<thead>
25+
<tr>
26+
<th>Name</th>
27+
<th>ID</th>
28+
<th>Owner</th>
29+
<th>Size</th>
30+
<th>Type</th>
31+
<th>Checksum</th>
32+
<th>Preview</th>
33+
</tr>
34+
</thead>
35+
<tbody>
36+
{{#each dataEntries}}
37+
<tr>
38+
<td>
39+
<a href="{{link}}?download=true">{{filename}}</a>
40+
<button type="button" class="close del-file" aria-hidden="true">&times;</button>
41+
</td>
42+
<td>{{id}}</td>
43+
<td>
44+
{{#if owner}}
45+
{{owner}}
46+
{{else}}
47+
<span class="text-muted">No owner</span>
48+
{{/if}}
49+
</td>
50+
<td>
51+
{{#if length}}
52+
{{formattedLength}}
53+
{{else}}
54+
{{uploadStatus}}
55+
<div class="progress">
56+
<div class="progress-bar" role="progressbar" min-width="60px" aria-valuenow="{{uploadProgress}}" aria-valuemin="0" aria-valuemax="100" style="width: {{uploadProgress}}%;">
57+
<span class="text-muted">{{uploadProgress}}%</span>
58+
</div>
59+
</div>
60+
{{/if}}
61+
</td>
62+
<td>{{contentType}}</td>
63+
<td>{{md5}}</td>
64+
<td>
65+
{{#if isImage}}
66+
<a href="{{link}}?x-auth-token={{loginToken}}"><img class="img-thumbnail" alt="[ preview of {{filename}} will display here ]" src="{{link}}" width="150"></a>
67+
{{else}}
68+
<span class="text-muted">No preview</span>
69+
{{/if}}
70+
</td>
71+
</tr>
72+
{{else}}
73+
<tr><td colspan="20" align=center>
74+
<span class="text-info">No files in collection, drag one here to upload</span>
75+
</td></tr>
76+
{{/each}}
77+
</tbody>
78+
</table>
79+
</div>
80+
</template>

Diff for: smart.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"packages": {
3+
"bootstrap-3": {},
4+
"jquery-cookie": {},
5+
"numeral": {},
6+
"jobCollection" : {},
7+
"fileCollection": {
8+
"git": "https://github.com/vsivsi/meteor-file-collection.git",
9+
"branch": "master"
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)