├── .gitignore
├── .versions
├── README.md
├── client
└── views
│ ├── display-image.html
│ ├── display-image.js
│ ├── upload-image.html
│ └── upload-image.js
├── examples
└── app
│ ├── .meteor
│ ├── .finished-upgraders
│ ├── .gitignore
│ ├── .id
│ ├── packages
│ ├── platforms
│ ├── release
│ └── versions
│ ├── README-example-app.md
│ ├── image-app.css
│ ├── image-app.html
│ ├── image-app.js
│ ├── packages
│ └── image-upload
│ └── settings-example.json
├── lib
└── image_upload.js
├── package.js
└── versions.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .env
3 | .build*
4 | /examples/*/settings.json
5 |
--------------------------------------------------------------------------------
/.versions:
--------------------------------------------------------------------------------
1 | babel-compiler@5.8.24_1
2 | babel-runtime@0.1.4
3 | base64@1.0.4
4 | binary-heap@1.0.4
5 | blaze@2.1.3
6 | blaze-tools@1.0.4
7 | boilerplate-generator@1.0.4
8 | caching-compiler@1.0.0
9 | caching-html-compiler@1.0.2
10 | callback-hook@1.0.4
11 | cfs:access-point@0.1.49
12 | cfs:base-package@0.0.30
13 | cfs:collection@0.5.5
14 | cfs:collection-filters@0.2.4
15 | cfs:data-man@0.0.6
16 | cfs:file@0.1.17
17 | cfs:graphicsmagick@0.0.18
18 | cfs:gridfs@0.0.33
19 | cfs:http-methods@0.0.29
20 | cfs:http-publish@0.0.13
21 | cfs:power-queue@0.9.11
22 | cfs:reactive-list@0.0.9
23 | cfs:reactive-property@0.0.4
24 | cfs:s3@0.1.3
25 | cfs:standard-packages@0.5.9
26 | cfs:storage-adapter@0.2.2
27 | cfs:tempstore@0.1.5
28 | cfs:upload-http@0.0.20
29 | cfs:worker@0.1.4
30 | check@1.1.0
31 | ddp@1.2.2
32 | ddp-client@1.2.1
33 | ddp-common@1.2.2
34 | ddp-server@1.2.2
35 | deps@1.0.9
36 | diff-sequence@1.0.1
37 | ecmascript@0.1.6
38 | ecmascript-runtime@0.2.6
39 | ejson@1.0.7
40 | geojson-utils@1.0.4
41 | html-tools@1.0.5
42 | htmljs@1.0.5
43 | http@1.1.1
44 | id-map@1.0.4
45 | jquery@1.11.4
46 | livedata@1.0.15
47 | logging@1.0.8
48 | meteor@1.1.10
49 | minifiers@1.1.7
50 | minimongo@1.0.10
51 | mongo@1.1.3
52 | mongo-id@1.0.1
53 | mongo-livedata@1.0.9
54 | npm-mongo@1.4.39_1
55 | observe-sequence@1.0.7
56 | okgrow:image-upload@0.8.3
57 | ordered-dict@1.0.4
58 | promise@0.5.1
59 | raix:eventemitter@0.1.2
60 | random@1.0.5
61 | reactive-dict@1.1.3
62 | reactive-var@1.0.6
63 | retry@1.0.4
64 | routepolicy@1.0.6
65 | session@1.1.1
66 | spacebars@1.0.7
67 | spacebars-compiler@1.0.7
68 | templating@1.1.5
69 | templating-tools@1.0.0
70 | tracker@1.0.9
71 | ui@1.0.8
72 | underscore@1.0.4
73 | url@1.0.5
74 | webapp@1.2.3
75 | webapp-hashing@1.0.5
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecated
2 |
3 | DISCLAIMER - ***Development of this package is currently on hiatus***. We are currently not actively developing this package due to both resource constraints and uncertainty about how well supported it will be in the future. We are using this in our active projects, so we will continue to do bug fixes as we encounter them, but feature requests may go unanswered. PRs are still welcome.
4 |
5 | # Image Upload for Meteor
6 |
7 | Image Upload makes it super easy for you to setup a photo input field along with all the trimmings (S3, collections, permissions, templates, etc.). Under the hood you will find ImageUpload is just a sugary wrapper around [collectionFS](https://atmospherejs.com/cfs).
8 |
9 | To get familiar with ImageUpload take a look at the rest of this read me as well as our example:
10 |
11 | [Example app live](http://ok-image-upload-demo.herokuapp.com/)
12 |
13 | [Example app source](https://github.com/okgrow/meteor-image-upload/blob/master/examples/app)
14 |
15 | ## One-time Setup
16 |
17 | ### Prerequisites
18 |
19 | 1. [AWS S3](http://aws.amazon.com/s3/) account for cloud file storage.
20 |
21 | 2. [GraphicsMagick](http://www.graphicsmagick.org/) or [ImageMagick](http://www.imagemagick.org/) on your local machine *and* deployment server for image manipulation.
22 |
23 | - **OS X:** `brew install imagemagick` or `brew install graphicsmagick`
24 |
25 | - **Modulus.io:** supports ImageMagick no setup needed
26 |
27 | - **Heroku, DigitalOcean, AWS EC2:** requires manual ImageMagick/GraphicsMagick installation.
28 |
29 | - **Meteor.com free hosting** does *not* support ImageMagick/GraphicsMagick, sorry no way around it :(
30 |
31 |
32 | ### Install & Configure
33 |
34 | Install from your terminal `meteor add okgrow:image-upload`.
35 |
36 | Configure in common code (*server* and *client* ).
37 |
38 | API: ` ImageUpload.configure( options ) `
39 |
40 | Example
41 | ```javascript
42 | ImageUpload.configure({
43 | accessKeyId: YOUR_ACCESS_KEY_ID,
44 | secretAccessKey: YOUR_SECRET_ACCESS_KEY,
45 | bucketName: YOUR_BUCKET_NAME,
46 | bucketUrl: YOUR_BUCKET_URL, //"https://your_bucket_name.s3.amazonaws.com/",
47 | maxUploadSize: 30 // MB
48 | });
49 | ```
50 |
51 | **WARNING** You should never store your keys publicly, instead use [Meteor.settings](http://docs.meteor.com/#/full/meteor_settings). Start your app using `meteor --settings settings.json`. Refer to our example app settings file to see how we do it.
52 |
53 | **Note** Since 0.8.0 `publicRead` is now set in the image collection options. ImageUpload will throw an error if you try and pass `publicRead` in `ImageUpload.configure()`
54 |
55 | ### Creating Image Collections
56 |
57 | The images you upload will be stored in separate *Image Upload* collections. You will probably have more than one Image Upload collection. The Image Upload collections are created differently from Meteor collections, we show you how to make these special collections below.
58 |
59 | Each Image Upload collection will reference and index it's documents to one of your app's data collections as specified, only one app data collection can be referenced per each Image Upload collection. We show you how to query by reference ids in [templating](#display-image)
60 | below.
61 |
62 | API: `ImageUpload.createCollection( name, reference, { [options] } )`
63 |
64 | Options:
65 |
66 | | Name | Optional | Description |
67 | | --- | :---: | --- |
68 | | **defaultPermissions** | optional | Enables default Allow rules on your image collection, see [Security Rules](#allowdeny-security-rules) to see the rules |
69 | | **publicRead** | optional | set to `true` to server files directly from S3, `bucketUrl` in `.configure()` is also required. Also allows visitors to view images if `defaultPermissions` is also true. |
70 | | **sizes** | optional | Let ImageMagick create multiple different sizes of each image automatically. Specify a size name as the key followed by an array for X,Y px lengths |
71 |
72 | The following creates an image collection called `userImages` which will be associated with the `Meteor.users` collection with images stored in four sizes:
73 |
74 | 1. The original image
75 | 2. "thumbnail": 200x200 px
76 | 3. "normal": 800x800 px
77 | 4. "large:": 1200x1200 px
78 |
79 | ```javascript
80 | UserImages = ImageUpload.createCollection("userImages", Meteor.users, {
81 | defaultPermissions: true,
82 | sizes: {
83 | thumbnail: [200, 200],
84 | normal: [800,800],
85 | large: [1200, 1200]
86 | }
87 | });
88 | ```
89 |
90 |
91 | ### Allow/Deny Security Rules
92 |
93 | Please add your own **allow/deny** rules and/or enable ImageUpload's `defaultPermissions` when creating the ImageUpload collection.
94 |
95 | defaultPermissions if enabled:
96 | ```javascript
97 | ImageCollection.allow({
98 | insert: function(userId, doc) {
99 | // Any authenticated user can create images
100 | return !!userId;
101 | },
102 | update: function(userId, doc) {
103 | // User can update their own image only
104 | return doc && doc.addedBy === userId;
105 | },
106 | remove: function(userId, doc) {
107 | // User can remove their own image only
108 | return doc && doc.addedBy === userId;
109 | },
110 | download: function (userId, fileObj) {
111 | // If publicRead has been set anyone can download, otherwise users
112 | // can only download images that they uploaded
113 | if (publicRead) {
114 | return true;
115 | } else {
116 | return fileObj.addedBy === userId;
117 | }
118 | }
119 | });
120 | ```
121 |
122 | *Note:* Since the image collection is based on `CollectionFS`, we use their `allow`
123 | and `deny` system. You can view their documentation here:
124 |
125 | https://github.com/CollectionFS/Meteor-CollectionFS#security
126 |
127 |
128 | ## Client-side Templating
129 |
130 | ### Upload Image Template
131 |
132 | You want a nice upload button with everything wired up for you? We got ya covered.
133 |
134 | API: `{{> uploadImage imageCollection=collectionName [option=option] }}`
135 |
136 | Examples:
137 | ```html
138 | {{> uploadImage imageCollection="userImages" size="thumbnail" doc=currentUser classImage="tiny-img round"}}
139 |
140 | {{> uploadImage imageCollection="postImages" name="post-image" size="banner" }}
141 | ```
142 |
143 | Attributes:
144 |
145 | | Name | Optional | Description |
146 | | --- | :---: | --- |
147 | | **imageCollection** | required | Specify the Image Upload collection images go to. *hint: This was the first parameter when creating the Image Upload collection.* |
148 | | **doc** | optional | When adding a new image to an existing document you can pass the existing document and we will make the reference for you. We pull the reference `_id` from the supplied object. |
149 | | **size** | optional | Specify the image size you want displayed when upload completes. By default this partial template displays the original uploaded image once complete. *hint: You made these sizes when creating your Image Upload collection.* |
150 | | **name** | optional | Specify a custom input element name. This overwrites the default input name attribute, `image` |
151 | | **classInput** | optional | Specify custom CSS class(es) for the input element. Included class is `image-file-picker` |
152 | | **classImage** | optional | Specify custom CSS class(es) for the image when it displays after upload completes. Included class is`uploaded-image` |
153 |
154 |
155 | ### Display Image
156 |
157 | To display a stored image, you can
158 |
159 | ```html
160 |
161 |
162 |
163 | ```
164 |
165 | Which uses a helper which loads a document from the image collection:
166 |
167 | ```javascript
168 | Template.yourTemplate.helpers({
169 | image: function() {
170 | var doc = Template.parentData(1);
171 | var image = yourImageCollection.findOne({associatedObjectId: doc._id});
172 | if (image) {
173 | return image.url({store: "userImages-thumbnail"});
174 | }
175 | }
176 | });
177 | ```
178 |
179 | ## Roadmap / TODO
180 |
181 | In order of fuzzy priority:
182 |
183 | - Less configuration to setup
184 | - Better error handling
185 | - Default upload progress bar
186 | - In-browser image cropping/resizing with darkroom.js
187 | - Upload files from a URL
188 |
189 | ### Contributing
190 |
191 | Issues and Pull Requests are always welcome. Please read our [contribution guidelines](https://github.com/okgrow/guides/blob/master/contributing.md).
192 |
193 | ### Direct Uploads to S3?
194 |
195 | At this point we don't have plans to support uploading files directly from
196 | the browser's client to AWS's S3. We may add this in the future, but there we
197 | will probably wait until CollectionFS supports this (they have it in the
198 | works). Pull requests welcome.
199 |
200 |
201 | Enjoy!
202 |
--------------------------------------------------------------------------------
/client/views/display-image.html:
--------------------------------------------------------------------------------
1 |
2 | {{#if thumbnail}}
3 |
4 | {{else}}
5 | {{#unless isUploaded}}
6 | Uploading ({{uploadProgress}})...
7 | {{else}}
8 | Processing...
9 | {{/unless}}
10 | {{/if}}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/client/views/display-image.js:
--------------------------------------------------------------------------------
1 | Template.displayImage.helpers({
2 | thumbnail: function () {
3 | var doc = Template.parentData(1);
4 | var store = doc.imageCollection + "-original";
5 | if(doc.size){
6 | store = doc.imageCollection + "-" + doc.size;
7 | }
8 | return this.url({store: store});
9 | }
10 | });
11 |
12 | Template.displayImage.events({
13 | "click [data-action=delete-image]": function () {
14 | this.collection.remove(this._id);
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/client/views/upload-image.html:
--------------------------------------------------------------------------------
1 |
2 | {{#if image}}
3 | {{> displayImage image}}
4 | {{else}}
5 |
6 | {{/if}}
7 |
8 |
--------------------------------------------------------------------------------
/client/views/upload-image.js:
--------------------------------------------------------------------------------
1 | Template.uploadImage.helpers({
2 | image: function() {
3 | var self = this;
4 | var coll = ImageUpload.getImageCollection(self.imageCollection);
5 | var store = self.imageCollection;
6 | var image;
7 |
8 | if (self.doc) {
9 | // Look for image for associated object
10 | image = coll.findOne({associatedObjectId: self.doc._id});
11 | } else {
12 | // No associated object yet, check id of last image of this type in session
13 | imageId = Session.get("lastImageId-" + store);
14 | image = coll.findOne({_id: imageId});
15 | }
16 | return image;
17 | },
18 | inputName: function(){
19 | return this.name || "image";
20 | }
21 | });
22 |
23 | Template.uploadImage.events({
24 | "change [data-action=image-file-picker]": function(event, template) {
25 | var self = this;
26 | var file = event.target.files[0];
27 | var coll = ImageUpload.getImageCollection(self.imageCollection);
28 | if (file) {
29 | var newFile = new FS.File(file);
30 | newFile.addedBy = Meteor.userId();
31 | if (self.doc) {
32 | newFile.associatedObjectId = self.doc._id;
33 | }
34 | coll.insert(newFile, function (err, fileObj) {
35 | if (err) {
36 | console.log("Error: ", err);
37 | }
38 | // Inserted new doc with ID fileObj._id, and kicked off the data upload using HTTP
39 | if (!self.associatedObjectId) {
40 | // Save the ID of the newly inserted doc in the session so we can use it
41 | // until it's associated.
42 | Session.set("lastImageId-" + self.imageCollection, fileObj._id);
43 | }
44 | });
45 | }
46 | }
47 | });
48 |
--------------------------------------------------------------------------------
/examples/app/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 | notices-for-facebook-graph-api-2
9 | 1.2.0-standard-minifiers-package
10 | 1.2.0-meteor-platform-split
11 | 1.2.0-cordova-changes
12 | 1.2.0-breaking-changes
13 | 1.3.0-split-minifiers-package
14 |
--------------------------------------------------------------------------------
/examples/app/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/examples/app/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | 19o0n6i1lytv8dahu0r
8 |
--------------------------------------------------------------------------------
/examples/app/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | accounts-ui
8 | okgrow:image-upload
9 | pauldowman:dotenv
10 | accounts-meteor-developer
11 | twbs:bootstrap
12 | meteor-base
13 | mobile-experience
14 | mongo
15 | blaze-html-templates
16 | session
17 | jquery
18 | tracker
19 | logging
20 | reload
21 | random
22 | ejson
23 | spacebars
24 | check
25 | standard-minifier-css
26 | standard-minifier-js
27 |
--------------------------------------------------------------------------------
/examples/app/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/examples/app/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.3.2.4
2 |
--------------------------------------------------------------------------------
/examples/app/.meteor/versions:
--------------------------------------------------------------------------------
1 | accounts-base@1.2.7
2 | accounts-meteor-developer@1.0.9
3 | accounts-oauth@1.1.12
4 | accounts-ui@1.1.9
5 | accounts-ui-unstyled@1.1.12
6 | allow-deny@1.0.4
7 | autoupdate@1.2.9
8 | babel-compiler@6.6.4
9 | babel-runtime@0.1.8
10 | base64@1.0.8
11 | binary-heap@1.0.8
12 | blaze@2.1.7
13 | blaze-html-templates@1.0.4
14 | blaze-tools@1.0.8
15 | boilerplate-generator@1.0.8
16 | caching-compiler@1.0.4
17 | caching-html-compiler@1.0.6
18 | callback-hook@1.0.8
19 | cfs:access-point@0.1.49
20 | cfs:base-package@0.0.30
21 | cfs:collection@0.5.5
22 | cfs:collection-filters@0.2.4
23 | cfs:data-man@0.0.6
24 | cfs:file@0.1.17
25 | cfs:graphicsmagick@0.0.18
26 | cfs:gridfs@0.0.33
27 | cfs:http-methods@0.0.32
28 | cfs:http-publish@0.0.13
29 | cfs:power-queue@0.9.11
30 | cfs:reactive-list@0.0.9
31 | cfs:reactive-property@0.0.4
32 | cfs:s3@0.1.3
33 | cfs:standard-packages@0.5.9
34 | cfs:storage-adapter@0.2.3
35 | cfs:tempstore@0.1.5
36 | cfs:upload-http@0.0.20
37 | cfs:worker@0.1.4
38 | check@1.2.1
39 | ddp@1.2.5
40 | ddp-client@1.2.7
41 | ddp-common@1.2.5
42 | ddp-rate-limiter@1.0.4
43 | ddp-server@1.2.6
44 | deps@1.0.12
45 | diff-sequence@1.0.5
46 | ecmascript@0.4.3
47 | ecmascript-runtime@0.2.10
48 | ejson@1.0.11
49 | fastclick@1.0.11
50 | geojson-utils@1.0.8
51 | hot-code-push@1.0.4
52 | html-tools@1.0.9
53 | htmljs@1.0.9
54 | http@1.1.5
55 | id-map@1.0.7
56 | jquery@1.11.8
57 | launch-screen@1.0.11
58 | less@2.6.0
59 | livedata@1.0.18
60 | localstorage@1.0.9
61 | logging@1.0.12
62 | meteor@1.1.14
63 | meteor-base@1.0.4
64 | meteor-developer@1.1.9
65 | minifier-css@1.1.11
66 | minifier-js@1.1.11
67 | minimongo@1.0.16
68 | mobile-experience@1.0.4
69 | mobile-status-bar@1.0.12
70 | modules@0.6.1
71 | modules-runtime@0.6.3
72 | mongo@1.1.7
73 | mongo-id@1.0.4
74 | mongo-livedata@1.0.12
75 | npm-mongo@1.4.43
76 | oauth@1.1.10
77 | oauth2@1.1.9
78 | observe-sequence@1.0.11
79 | okgrow:image-upload@0.8.3
80 | ordered-dict@1.0.7
81 | pauldowman:dotenv@1.0.1
82 | promise@0.6.7
83 | raix:eventemitter@0.1.3
84 | random@1.0.9
85 | rate-limit@1.0.4
86 | reactive-dict@1.1.7
87 | reactive-var@1.0.9
88 | reload@1.1.8
89 | retry@1.0.7
90 | routepolicy@1.0.10
91 | service-configuration@1.0.9
92 | session@1.1.5
93 | spacebars@1.0.11
94 | spacebars-compiler@1.0.11
95 | standard-minifier-css@1.0.6
96 | standard-minifier-js@1.0.6
97 | templating@1.1.9
98 | templating-tools@1.0.4
99 | tracker@1.0.13
100 | twbs:bootstrap@3.3.6
101 | ui@1.0.11
102 | underscore@1.0.8
103 | url@1.0.9
104 | webapp@1.2.8
105 | webapp-hashing@1.0.9
106 |
--------------------------------------------------------------------------------
/examples/app/README-example-app.md:
--------------------------------------------------------------------------------
1 | Image Upload Demo App
2 | ---------------------
3 |
4 | Note that the demo app is purged of data on the hour every hour.
5 |
6 | Running this app yourself
7 | -------------------------
8 |
9 | You will need GraphicsMagick installed.
10 |
11 | To install on OS X: `brew install graphicsmagick`
12 |
13 | Copy `settings-example.json` to `settings.json` and edit with your actual
14 | AWS credentials and bucket name.
15 |
16 | Start the app: `meteor --settings settings.json`
17 |
18 | Deploying this app
19 | ------------------
20 |
21 | Use Heroku. You can't use Meteor.com's deployment environment because it does
22 | not support GraphicsMagick. (We have a [branch](https://github.com/okgrow/meteor-image-upload/tree/heroku-deploy)
23 | that contains the example app at the root for deploying to Heroku, we only merge
24 | one way from master to that branch.)
25 |
--------------------------------------------------------------------------------
/examples/app/image-app.css:
--------------------------------------------------------------------------------
1 | /* CSS declarations go here */
2 |
--------------------------------------------------------------------------------
/examples/app/image-app.html:
--------------------------------------------------------------------------------
1 |
Note that all data (users, posts, images) are purged periodically. If everything disappears while you're viewing this, that's what has happened.
17 |You can view this app's source code.
18 |35 | Choose a profile avatar: 36 | {{> uploadImage imageCollection="userImages" doc=currentUser classImage="image-round" classInput="image-picker" inputName="user-image"}} 37 |
38 | 39 | 40 | 41 |42 | All users on this site: 43 |
91 | Messages: 92 |