spree_essentials 0.1.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +23 -0
- data/README.md +187 -0
- data/Rakefile +40 -0
- data/app/controllers/admin/markdown_controller.rb +7 -0
- data/app/controllers/admin/resource_controller.rb +222 -0
- data/app/controllers/admin/uploads_controller.rb +34 -0
- data/app/helpers/admin/base_helper_decorator.rb +18 -0
- data/app/models/image_decorator.rb +7 -0
- data/app/models/upload.rb +46 -0
- data/app/validators/datetime_validator.rb +6 -0
- data/app/views/admin/shared/_contents_sub_menu.html.erb +11 -0
- data/app/views/admin/shared/_contents_tab.html.erb +2 -0
- data/app/views/admin/uploads/_form.html.erb +8 -0
- data/app/views/admin/uploads/edit.html.erb +18 -0
- data/app/views/admin/uploads/index.html.erb +50 -0
- data/app/views/admin/uploads/new.html.erb +21 -0
- data/app/views/admin/uploads/picker.html.erb +11 -0
- data/config/locales/en.yml +79 -0
- data/config/routes.rb +10 -0
- data/lib/generators/essentials_base.rb +23 -0
- data/lib/generators/spree_essentials/install_generator.rb +16 -0
- data/lib/generators/templates/db/migrate/add_attachment_file_size_to_assets.rb +9 -0
- data/lib/spree_core/action_callbacks.rb +26 -0
- data/lib/spree_essentials/test_helper.rb +21 -0
- data/lib/spree_essentials/version.rb +3 -0
- data/lib/spree_essentials.rb +52 -0
- data/public/images/admin/icons/pages.png +0 -0
- data/public/images/blog/rss.png +0 -0
- data/public/images/markitup/bg-container.png +0 -0
- data/public/images/markitup/bg-editor.png +0 -0
- data/public/images/markitup/bg-picker.png +0 -0
- data/public/images/markitup/bold.png +0 -0
- data/public/images/markitup/code.png +0 -0
- data/public/images/markitup/h1.png +0 -0
- data/public/images/markitup/h2.png +0 -0
- data/public/images/markitup/h3.png +0 -0
- data/public/images/markitup/h4.png +0 -0
- data/public/images/markitup/h5.png +0 -0
- data/public/images/markitup/h6.png +0 -0
- data/public/images/markitup/handle.png +0 -0
- data/public/images/markitup/image-picker.png +0 -0
- data/public/images/markitup/italic.png +0 -0
- data/public/images/markitup/link.png +0 -0
- data/public/images/markitup/list-bullet.png +0 -0
- data/public/images/markitup/list-numeric.png +0 -0
- data/public/images/markitup/menu.png +0 -0
- data/public/images/markitup/more-tag.png +0 -0
- data/public/images/markitup/picture.png +0 -0
- data/public/images/markitup/preview.png +0 -0
- data/public/images/markitup/quotes.png +0 -0
- data/public/images/markitup/submenu.png +0 -0
- data/public/javascripts/date.js +145 -0
- data/public/javascripts/jquery.autodate.js +112 -0
- data/public/javascripts/jquery.markitup.js +574 -0
- data/public/javascripts/markdown.set.js +95 -0
- data/public/stylesheets/essentials.css +35 -0
- data/public/stylesheets/markitup.css +278 -0
- metadata +245 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Redistribution and use in source and binary forms, with or without modification,
|
2
|
+
are permitted provided that the following conditions are met:
|
3
|
+
|
4
|
+
* Redistributions of source code must retain the above copyright notice,
|
5
|
+
this list of conditions and the following disclaimer.
|
6
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
7
|
+
this list of conditions and the following disclaimer in the documentation
|
8
|
+
and/or other materials provided with the distribution.
|
9
|
+
* Neither the name of the Rails Dog LLC nor the names of its
|
10
|
+
contributors may be used to endorse or promote products derived from this
|
11
|
+
software without specific prior written permission.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
14
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
15
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
16
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
17
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
19
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
20
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
21
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
22
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
23
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
Spree Essentials
|
2
|
+
================
|
3
|
+
|
4
|
+
Spree Essentials is the base for many content related extensions for Spree. It doesn't do much on it's own ;)
|
5
|
+
|
6
|
+
Spree Essentials provides other extensions with:
|
7
|
+
|
8
|
+
* An easy markdown editor with ajax preview
|
9
|
+
* Image picker for embedding images into markdown editor
|
10
|
+
|
11
|
+
Current essential-aware extensions include:
|
12
|
+
|
13
|
+
* [spree_essential_cms](https://github.com/citrus/spree_essential_cms): A full featured CMS with pages, contents, images and more
|
14
|
+
* [spree_essential_blog](https://github.com/citrus/spree_essential_blog): A blog complete with archives, tags and related products
|
15
|
+
* [spree_essential_news](https://github.com/citrus/spree_essential_news): A news system, also complete with archives, tags and related products. (yes it's pretty much the exact same thing as the blog)
|
16
|
+
* [spree_essential_press](https://github.com/citrus/spree_essential_press): A simple press page for displaying media related content.
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
**ALL OF THESE UNDER DEVELOPMENT**
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
Installation
|
25
|
+
------------
|
26
|
+
|
27
|
+
If you don't already have an existing Spree site, [click here](https://gist.github.com/946719) then come back later... You can also read the Spree docs [here](http://spreecommerce.com/documentation/getting_started.html)...
|
28
|
+
|
29
|
+
Spree Essentials hasn't been released to rubygems so you'll have to install it from the source. Just add the following to your Gemfile:
|
30
|
+
|
31
|
+
gem 'spree_essentials', :git => 'git://github.com/citrus/spree_essentials.git'
|
32
|
+
|
33
|
+
# Add any of the extensions you wish to use
|
34
|
+
|
35
|
+
gem 'spree_essential_cms', :git => 'git://github.com/citrus/spree_essential_cms.git'
|
36
|
+
gem 'spree_essential_blog', :git => 'git://github.com/citrus/spree_essential_blog.git'
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
Then run:
|
41
|
+
|
42
|
+
bundle install
|
43
|
+
|
44
|
+
|
45
|
+
Once that's complete, run the migration generator and migrate your database:
|
46
|
+
|
47
|
+
To see your available generators run
|
48
|
+
|
49
|
+
rails g
|
50
|
+
|
51
|
+
|
52
|
+
Now run the generators for extensions you wish to install
|
53
|
+
|
54
|
+
rails g spree_essentials:install
|
55
|
+
rails g spree_essentials:cms
|
56
|
+
rails g spree_essentials:blog
|
57
|
+
|
58
|
+
|
59
|
+
Then migrate your database:
|
60
|
+
rake db:migrate
|
61
|
+
|
62
|
+
|
63
|
+
If that all went smoothly, you should be ready to boot the server with:
|
64
|
+
|
65
|
+
rails s
|
66
|
+
|
67
|
+
|
68
|
+
Now login to the admin and click on the 'Content' tab!
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
Essential Awareness
|
73
|
+
-------------------
|
74
|
+
|
75
|
+
Spree Essentials is designed to allow other extensions to reside under it's global 'Content' tab in the admin. [SpreeEssentialPress](https://github.com/citrus/spree_essential_press) is the first of many extensions that can run with or without spree_essentials installed.
|
76
|
+
|
77
|
+
Setting up an essential aware extension is easy. In your `lib/[extension_name].rb` file, add something like this:
|
78
|
+
|
79
|
+
|
80
|
+
module SpreeEssentialPress
|
81
|
+
|
82
|
+
def self.tab
|
83
|
+
[:press, { :route => :admin_press_index }]
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.sub_tab
|
87
|
+
[:press, { :route => :admin_press_index, :label => 'admin.subnav.press', :match_path => '/press' }]
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.independent?
|
91
|
+
return true unless defined?(SpreeEssentials)
|
92
|
+
!SpreeEssentials.respond_to(:register)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
if SpreeEssentialPress.independent?
|
98
|
+
require 'spree_essential_press/custom_hooks'
|
99
|
+
else
|
100
|
+
SpreeEssentials.register :press, SpreeEssentialPress
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
Notes
|
107
|
+
-----
|
108
|
+
|
109
|
+
As I mentioned before, spree_essentials is still being developed... Development is being done on OSX with Ruby 1.9.2 and Spree 0.50.2.
|
110
|
+
|
111
|
+
Please let me know of any bugs you find or feature requests you'd like to see.
|
112
|
+
|
113
|
+
|
114
|
+
Testing
|
115
|
+
-------
|
116
|
+
|
117
|
+
The test suite is very limited at the moment but can be run like so:
|
118
|
+
|
119
|
+
git clone git://github.com/citrus/spree_essentials.git
|
120
|
+
cd spree_essentials
|
121
|
+
bundle install
|
122
|
+
rake db:test_prep
|
123
|
+
rake
|
124
|
+
|
125
|
+
|
126
|
+
You can also boot spork to run the tests much faster
|
127
|
+
|
128
|
+
# in one window
|
129
|
+
cd spree_essentials
|
130
|
+
spork
|
131
|
+
|
132
|
+
# in another window
|
133
|
+
testdrb test/**/*_test.rb
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
To Do
|
139
|
+
-----
|
140
|
+
|
141
|
+
* more tests
|
142
|
+
* better documentation
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
Change Log
|
148
|
+
----------
|
149
|
+
|
150
|
+
|
151
|
+
**2011/4/26**
|
152
|
+
|
153
|
+
* Modularized CMS. It can now be found [here](https://github.com/citrus/spree_essential_cms)
|
154
|
+
|
155
|
+
**2011/4/12**
|
156
|
+
|
157
|
+
* Added content contexts for multiple contents per page
|
158
|
+
* Namespaced posts into blog/posts
|
159
|
+
* Added `<!-- more -->` tag to posts
|
160
|
+
|
161
|
+
|
162
|
+
**2011/4/4**
|
163
|
+
|
164
|
+
* Added Page Images and starting to remove resource_controller.
|
165
|
+
|
166
|
+
|
167
|
+
**2011/4/3**
|
168
|
+
|
169
|
+
* Automatically adds [Heroku](http://heroku.com) support when you include [spree_heroku](https://github.com/paxer/spree-heroku) in your Gemfile. Check the [spree_heroku readme](https://github.com/paxer/spree-heroku#readme) for full installation instructions.
|
170
|
+
* Creates default content when you create a new page.
|
171
|
+
|
172
|
+
more in `CHANGELOG.md`
|
173
|
+
|
174
|
+
|
175
|
+
Contributors
|
176
|
+
------------
|
177
|
+
|
178
|
+
So far it's just me; Spencer Steffen.
|
179
|
+
|
180
|
+
If you'd like to help out feel free to fork and send me pull requests!
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
License
|
185
|
+
-------
|
186
|
+
|
187
|
+
Copyright (c) 2011 Spencer Steffen, released under the New BSD License All rights reserved.
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must run `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
require 'rake/testtask'
|
12
|
+
|
13
|
+
Bundler::GemHelper.install_tasks
|
14
|
+
|
15
|
+
Rake::TestTask.new(:test) do |t|
|
16
|
+
t.libs << 'lib'
|
17
|
+
t.libs << 'test'
|
18
|
+
t.pattern = 'test/**/*_test.rb'
|
19
|
+
t.verbose = false
|
20
|
+
end
|
21
|
+
|
22
|
+
namespace :db do
|
23
|
+
desc "preps the test database"
|
24
|
+
task :test_prep do
|
25
|
+
dir = File.expand_path("../test/dummy/db", __FILE__)
|
26
|
+
db = File.join(dir, "test.sqlite3")
|
27
|
+
File.delete(db) if File.exists?(db)
|
28
|
+
system("cd test/dummy; rake db:migrate RAILS_ENV=test")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
task :default => :test
|
33
|
+
|
34
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
35
|
+
rdoc.rdoc_dir = 'rdoc'
|
36
|
+
rdoc.title = 'SpreeEssentials'
|
37
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
38
|
+
rdoc.rdoc_files.include('README.rdoc')
|
39
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
40
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'spree_core/action_callbacks'
|
2
|
+
|
3
|
+
class Admin::ResourceController < Admin::BaseController
|
4
|
+
helper_method :new_object_url, :edit_object_url, :object_url, :collection_url
|
5
|
+
before_filter :load_resource
|
6
|
+
|
7
|
+
respond_to :html
|
8
|
+
|
9
|
+
def new
|
10
|
+
render :layout => !request.xhr?
|
11
|
+
end
|
12
|
+
|
13
|
+
def edit
|
14
|
+
render :layout => !request.xhr?
|
15
|
+
end
|
16
|
+
|
17
|
+
def update
|
18
|
+
invoke_callbacks(:update, :before)
|
19
|
+
if @object.update_attributes(params[object_name])
|
20
|
+
invoke_callbacks(:update, :after)
|
21
|
+
resource_desc = I18n.t(object_name)
|
22
|
+
resource_desc += " \"#{@object.name}\"" if @object.respond_to?(:name)
|
23
|
+
flash[:notice] = I18n.t(:successfully_updated, :resource => resource_desc)
|
24
|
+
respond_to do |format|
|
25
|
+
format.html { redirect_to location_after_save }
|
26
|
+
format.js { render :layout => false }
|
27
|
+
end
|
28
|
+
else
|
29
|
+
invoke_callbacks(:update, :fails)
|
30
|
+
render :edit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create
|
35
|
+
invoke_callbacks(:create, :before)
|
36
|
+
if @object.save
|
37
|
+
invoke_callbacks(:create, :after)
|
38
|
+
resource_desc = I18n.t(object_name)
|
39
|
+
resource_desc += " \"#{@object.name}\"" if @object.respond_to?(:name)
|
40
|
+
flash[:notice] = I18n.t(:successfully_created, :resource => resource_desc)
|
41
|
+
respond_to do |format|
|
42
|
+
format.html { redirect_to location_after_save }
|
43
|
+
format.js { render :layout => false }
|
44
|
+
end
|
45
|
+
else
|
46
|
+
invoke_callbacks(:create, :fails)
|
47
|
+
render :new
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def destroy
|
52
|
+
invoke_callbacks(:destroy, :before)
|
53
|
+
if @object.destroy
|
54
|
+
invoke_callbacks(:destroy, :after)
|
55
|
+
resource_desc = I18n.t(object_name)
|
56
|
+
resource_desc += " \"#{@object.name}\"" if @object.respond_to?(:name)
|
57
|
+
flash[:notice] = I18n.t(:successfully_removed, :resource => resource_desc)
|
58
|
+
respond_to do |format|
|
59
|
+
format.html { redirect_to collection_url }
|
60
|
+
format.js { render :partial => "/admin/shared/destroy" }
|
61
|
+
end
|
62
|
+
else
|
63
|
+
invoke_callbacks(:destroy, :fails)
|
64
|
+
redirect_to collection_url
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
class << self
|
71
|
+
attr_accessor :parent_data
|
72
|
+
attr_accessor :callbacks
|
73
|
+
|
74
|
+
def belongs_to(model_name, options = {})
|
75
|
+
@parent_data ||= {}
|
76
|
+
@parent_data[:model_name] = model_name
|
77
|
+
@parent_data[:model_class] = model_name.to_s.classify.constantize
|
78
|
+
@parent_data[:find_by] = options[:find_by] || :id
|
79
|
+
end
|
80
|
+
|
81
|
+
def create
|
82
|
+
@callbacks ||= {}
|
83
|
+
@callbacks[:create] ||= Spree::ActionCallbacks.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def update
|
87
|
+
@callbacks ||= {}
|
88
|
+
@callbacks[:update] ||= Spree::ActionCallbacks.new
|
89
|
+
end
|
90
|
+
|
91
|
+
def destroy
|
92
|
+
@callbacks ||= {}
|
93
|
+
@callbacks[:destroy] ||= Spree::ActionCallbacks.new
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def model_class
|
98
|
+
controller_name.classify.constantize
|
99
|
+
end
|
100
|
+
|
101
|
+
def object_name
|
102
|
+
controller_name.singularize
|
103
|
+
end
|
104
|
+
|
105
|
+
def load_resource
|
106
|
+
if member_action?
|
107
|
+
@object ||= load_resource_instance
|
108
|
+
instance_variable_set("@#{object_name}", @object)
|
109
|
+
else
|
110
|
+
@collection ||= collection
|
111
|
+
instance_variable_set("@#{controller_name}", @collection)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_resource_instance
|
116
|
+
if new_actions.include?(params[:action].to_sym)
|
117
|
+
build_resource
|
118
|
+
elsif params[:id]
|
119
|
+
find_resource
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def parent_data
|
124
|
+
self.class.parent_data
|
125
|
+
end
|
126
|
+
|
127
|
+
def parent
|
128
|
+
if parent_data.present?
|
129
|
+
@parent ||= parent_data[:model_class].where(parent_data[:find_by] => params["#{parent_data[:model_name]}_id"]).first
|
130
|
+
instance_variable_set("@#{parent_data[:model_name]}", @parent)
|
131
|
+
else
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_resource
|
137
|
+
if parent_data.present?
|
138
|
+
parent.send(controller_name).find(params[:id])
|
139
|
+
else
|
140
|
+
model_class.find(params[:id])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def build_resource
|
145
|
+
if parent_data.present?
|
146
|
+
parent.send(controller_name).build(params[object_name])
|
147
|
+
else
|
148
|
+
model_class.new(params[object_name])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def collection
|
153
|
+
return parent.send(controller_name) if parent_data.present?
|
154
|
+
|
155
|
+
if model_class.respond_to?(:accessible_by) && !current_ability.has_block?(params[:action], model_class)
|
156
|
+
model_class.accessible_by(current_ability)
|
157
|
+
else
|
158
|
+
model_class.scoped
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def location_after_save
|
163
|
+
collection_url
|
164
|
+
end
|
165
|
+
|
166
|
+
def invoke_callbacks(action, callback_type)
|
167
|
+
callbacks = self.class.callbacks || {}
|
168
|
+
return if callbacks[action].nil?
|
169
|
+
case callback_type.to_sym
|
170
|
+
when :before then callbacks[action].before_methods.each {|method| send method }
|
171
|
+
when :after then callbacks[action].after_methods.each {|method| send method }
|
172
|
+
when :fails then callbacks[action].fails_methods.each {|method| send method }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# URL helpers
|
177
|
+
|
178
|
+
def new_object_url(options = {})
|
179
|
+
if parent_data.present?
|
180
|
+
new_polymorphic_url([:admin, parent, model_class], options)
|
181
|
+
else
|
182
|
+
new_polymorphic_url([:admin, model_class], options)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def edit_object_url(object, options = {})
|
187
|
+
if parent_data.present?
|
188
|
+
send "edit_admin_#{parent_data[:model_name]}_#{object_name}_url", parent, object, options
|
189
|
+
else
|
190
|
+
send "edit_admin_#{object_name}_url", object, options
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def object_url(object = nil, options = {})
|
195
|
+
target = object ? object : @object
|
196
|
+
if parent_data.present?
|
197
|
+
send "admin_#{parent_data[:model_name]}_#{object_name}_url", parent, target, options
|
198
|
+
else
|
199
|
+
send "admin_#{object_name}_url", target, options
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def collection_url(options = {})
|
204
|
+
if parent_data.present?
|
205
|
+
polymorphic_url([:admin, parent, model_class], options)
|
206
|
+
else
|
207
|
+
polymorphic_url([:admin, model_class], options)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def collection_actions
|
212
|
+
[:index]
|
213
|
+
end
|
214
|
+
|
215
|
+
def member_action?
|
216
|
+
!collection_actions.include? params[:action].to_sym
|
217
|
+
end
|
218
|
+
|
219
|
+
def new_actions
|
220
|
+
[:new, :create]
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Admin::UploadsController < Admin::BaseController
|
2
|
+
|
3
|
+
resource_controller
|
4
|
+
|
5
|
+
index.response do |wants|
|
6
|
+
wants.html { render :template => request.xhr? ? 'admin/uploads/picker' : 'admin/uploads/index', :layout => !request.xhr? }
|
7
|
+
end
|
8
|
+
|
9
|
+
new_action.response do |wants|
|
10
|
+
wants.html {
|
11
|
+
render :action => :new, :layout => false
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
create.response do |wants|
|
16
|
+
wants.html {redirect_to admin_uploads_path}
|
17
|
+
end
|
18
|
+
|
19
|
+
update.response do |wants|
|
20
|
+
wants.html { redirect_to collection_url }
|
21
|
+
end
|
22
|
+
|
23
|
+
destroy.success.wants.js { render_js_for_destroy }
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def collection
|
28
|
+
params[:search] ||= {}
|
29
|
+
params[:search][:meta_sort] ||= "posted_at.desc"
|
30
|
+
@search = end_of_association_chain.metasearch(params[:search])
|
31
|
+
@collection = @search.paginate(:per_page => Spree::Config[:orders_per_page], :page => params[:page])
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Spree::BaseHelper.class_eval do
|
2
|
+
|
3
|
+
def markdown_helper
|
4
|
+
content_tag('em', :class => 'small') do
|
5
|
+
[
|
6
|
+
" ",
|
7
|
+
t('essentials.parsed_with'),
|
8
|
+
link_to("Markdown", "http://daringfireball.net/projects/markdown/basics", :onclick => 'window.open(this.href); return false')
|
9
|
+
].join(" ").html_safe
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method, *args, &block)
|
14
|
+
return super unless method.to_s =~ /_path$/
|
15
|
+
"/" + method.to_s.sub(/_path$/, '').gsub('_', '-')
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Upload < Asset
|
2
|
+
|
3
|
+
default_scope where(:type => "Upload")
|
4
|
+
|
5
|
+
validate :no_attachement_errors
|
6
|
+
|
7
|
+
# Check for spree_heroku
|
8
|
+
# https://github.com/paxer/spree-heroku
|
9
|
+
#
|
10
|
+
if defined?(SpreeHeroku)
|
11
|
+
has_attached_file :attachment,
|
12
|
+
:styles => Proc.new{ |clip| clip.instance.attachment_sizes },
|
13
|
+
:default_style => :medium,
|
14
|
+
:path => "assets/uploads/:id/:style/:basename.:extension",
|
15
|
+
:storage => "s3",
|
16
|
+
:s3_credentials => "#{Rails.root}/config/s3.yml"
|
17
|
+
else
|
18
|
+
has_attached_file :attachment,
|
19
|
+
:styles => Proc.new{ |clip| clip.instance.attachment_sizes },
|
20
|
+
:default_style => :medium,
|
21
|
+
:url => "/assets/uploads/:id/:style/:basename.:extension",
|
22
|
+
:path => ":rails_root/public/assets/uploads/:id/:style/:basename.:extension"
|
23
|
+
end
|
24
|
+
|
25
|
+
def image_content?
|
26
|
+
attachment_content_type.match(/\/(jpeg|png|gif|tiff|x-photoshop)/)
|
27
|
+
end
|
28
|
+
|
29
|
+
def attachment_sizes
|
30
|
+
if image_content?
|
31
|
+
{ :mini => '48x48>', :small => '150x150>', :medium => '420x300>', :large => '800x500>' }
|
32
|
+
else
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def no_attachement_errors
|
38
|
+
if attachment_file_name.blank? || !attachment.errors.empty?
|
39
|
+
# uncomment this to get rid of the less-than-useful interrim messages
|
40
|
+
errors.clear
|
41
|
+
errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file."
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class DatetimeValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
date = DateTime.parse(value.to_s) rescue ArgumentError
|
4
|
+
record.errors.add(attribute, I18n.t(:invalid_date_time, :scope => [:activerecord, :errors, :messages])) if date == ArgumentError
|
5
|
+
end
|
6
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% content_for :sub_menu do %>
|
2
|
+
<ul id="sub_nav">
|
3
|
+
<%= hook :admin_contents_sub_tabs do %>
|
4
|
+
<%= tab :pages, :label => 'admin.subnav.pages', :match_path => '/pages' %>
|
5
|
+
<% SpreeEssentials.essentials.each do |cls| %>
|
6
|
+
<%= tab *cls.sub_tab if defined?(cls.sub_tab) %>
|
7
|
+
<% end %>
|
8
|
+
<%= tab :uploads, :label => 'admin.subnav.uploads' %>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= render :partial => 'admin/shared/contents_sub_menu' %>
|
2
|
+
|
3
|
+
<% if @upload.try(:errors).present? %>
|
4
|
+
<%= render 'shared/error_messages', :target => @upload %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<% form_for([:admin, @upload], :url => admin_upload_url(@upload), :html => { :multipart => true }) do |f| %>
|
8
|
+
<p>
|
9
|
+
<%= label_tag ("thumbnail") %>:<br/>
|
10
|
+
<%= link_to(image_tag(@upload.attachment.url(:mini)), @upload.attachment.url(:product)) %>
|
11
|
+
</p>
|
12
|
+
<%= render "form", :form => f %>
|
13
|
+
</table>
|
14
|
+
<p class="form-buttons">
|
15
|
+
<%= button t("update") %>
|
16
|
+
or <%= link_to t("cancel"), admin_uploads_url, :id => "cancel_link" %>
|
17
|
+
</p>
|
18
|
+
<% end %>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<%= render :partial => 'admin/shared/contents_sub_menu' %>
|
2
|
+
|
3
|
+
<table class="index">
|
4
|
+
<tr>
|
5
|
+
<th><%= t("thumbnail") %></th>
|
6
|
+
<th><%= sort_link @search, :attachment_file_name, t("upload.file_name") %></th>
|
7
|
+
<th><%= sort_link @search, :description, t("description") %></th>
|
8
|
+
<th><%= sort_link @search, :attachment_file_size, t("upload.size") %></th>
|
9
|
+
<th><%= t("action") %></th>
|
10
|
+
</tr>
|
11
|
+
|
12
|
+
<% @uploads.each do |upload| %>
|
13
|
+
<tr id="<%= dom_id(upload) %>">
|
14
|
+
<td class="thumbnail">
|
15
|
+
<% if upload.image_content? %>
|
16
|
+
<%= link_to image_tag(upload.attachment.url(:mini)), upload.attachment.url(:large) %>
|
17
|
+
<% end %>
|
18
|
+
</td>
|
19
|
+
<td><%= link_to upload.attachment_file_name, upload.attachment.url(:original) %></td>
|
20
|
+
<td><%= upload.alt %></td>
|
21
|
+
<td><%= upload.attachment_file_size.to_i / 1024 %> kb</td>
|
22
|
+
<td class="actions">
|
23
|
+
<%= link_to_with_icon('edit', t("edit"), edit_admin_upload_url(upload)) %>
|
24
|
+
|
25
|
+
<%= link_to_delete upload, {:url => admin_upload_url(upload) }%>
|
26
|
+
</td>
|
27
|
+
</tr>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
</table>
|
31
|
+
|
32
|
+
<div id="images"></div>
|
33
|
+
<br/>
|
34
|
+
<p>
|
35
|
+
<%= link_to icon('add') + ' ' + t(".new_upload"), new_admin_upload_url, :id => "new_image_link" %>
|
36
|
+
</p>
|
37
|
+
|
38
|
+
<% content_for :head do %>
|
39
|
+
<script type="text/javascript">
|
40
|
+
jQuery(document).ready(function(){
|
41
|
+
|
42
|
+
jQuery('#new_image_link').click(function(event) {
|
43
|
+
event.preventDefault();
|
44
|
+
jQuery(this).hide();
|
45
|
+
jQuery.ajax({type: 'GET', url: this.href, data: ({authenticity_token: AUTH_TOKEN}), success: function(r){ jQuery('#images').html(r);} });
|
46
|
+
});
|
47
|
+
|
48
|
+
});
|
49
|
+
</script>
|
50
|
+
<% end %>
|