spree_essentials 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/LICENSE +23 -0
  2. data/README.md +187 -0
  3. data/Rakefile +40 -0
  4. data/app/controllers/admin/markdown_controller.rb +7 -0
  5. data/app/controllers/admin/resource_controller.rb +222 -0
  6. data/app/controllers/admin/uploads_controller.rb +34 -0
  7. data/app/helpers/admin/base_helper_decorator.rb +18 -0
  8. data/app/models/image_decorator.rb +7 -0
  9. data/app/models/upload.rb +46 -0
  10. data/app/validators/datetime_validator.rb +6 -0
  11. data/app/views/admin/shared/_contents_sub_menu.html.erb +11 -0
  12. data/app/views/admin/shared/_contents_tab.html.erb +2 -0
  13. data/app/views/admin/uploads/_form.html.erb +8 -0
  14. data/app/views/admin/uploads/edit.html.erb +18 -0
  15. data/app/views/admin/uploads/index.html.erb +50 -0
  16. data/app/views/admin/uploads/new.html.erb +21 -0
  17. data/app/views/admin/uploads/picker.html.erb +11 -0
  18. data/config/locales/en.yml +79 -0
  19. data/config/routes.rb +10 -0
  20. data/lib/generators/essentials_base.rb +23 -0
  21. data/lib/generators/spree_essentials/install_generator.rb +16 -0
  22. data/lib/generators/templates/db/migrate/add_attachment_file_size_to_assets.rb +9 -0
  23. data/lib/spree_core/action_callbacks.rb +26 -0
  24. data/lib/spree_essentials/test_helper.rb +21 -0
  25. data/lib/spree_essentials/version.rb +3 -0
  26. data/lib/spree_essentials.rb +52 -0
  27. data/public/images/admin/icons/pages.png +0 -0
  28. data/public/images/blog/rss.png +0 -0
  29. data/public/images/markitup/bg-container.png +0 -0
  30. data/public/images/markitup/bg-editor.png +0 -0
  31. data/public/images/markitup/bg-picker.png +0 -0
  32. data/public/images/markitup/bold.png +0 -0
  33. data/public/images/markitup/code.png +0 -0
  34. data/public/images/markitup/h1.png +0 -0
  35. data/public/images/markitup/h2.png +0 -0
  36. data/public/images/markitup/h3.png +0 -0
  37. data/public/images/markitup/h4.png +0 -0
  38. data/public/images/markitup/h5.png +0 -0
  39. data/public/images/markitup/h6.png +0 -0
  40. data/public/images/markitup/handle.png +0 -0
  41. data/public/images/markitup/image-picker.png +0 -0
  42. data/public/images/markitup/italic.png +0 -0
  43. data/public/images/markitup/link.png +0 -0
  44. data/public/images/markitup/list-bullet.png +0 -0
  45. data/public/images/markitup/list-numeric.png +0 -0
  46. data/public/images/markitup/menu.png +0 -0
  47. data/public/images/markitup/more-tag.png +0 -0
  48. data/public/images/markitup/picture.png +0 -0
  49. data/public/images/markitup/preview.png +0 -0
  50. data/public/images/markitup/quotes.png +0 -0
  51. data/public/images/markitup/submenu.png +0 -0
  52. data/public/javascripts/date.js +145 -0
  53. data/public/javascripts/jquery.autodate.js +112 -0
  54. data/public/javascripts/jquery.markitup.js +574 -0
  55. data/public/javascripts/markdown.set.js +95 -0
  56. data/public/stylesheets/essentials.css +35 -0
  57. data/public/stylesheets/markitup.css +278 -0
  58. 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,7 @@
1
+ class Admin::MarkdownController < Admin::BaseController
2
+
3
+ def preview
4
+ render :text => RDiscount.new(params[:data].to_s).to_html
5
+ end
6
+
7
+ 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
+ "&nbsp;",
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,7 @@
1
+ Image.class_eval do
2
+
3
+ def has_alt?
4
+ !alt.blank?
5
+ end
6
+
7
+ 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,2 @@
1
+ <%= tab *[:pages, :contents, SpreeEssentials.essentials.map{|cls| cls.tab if defined?(cls.tab) }, :uploads, { :label => '.content' }].flatten %>
2
+
@@ -0,0 +1,8 @@
1
+ <p>
2
+ <%= form.label :attachment %>:<br/>
3
+ <%= form.file_field :attachment %>
4
+ </p>
5
+ <p>
6
+ <%= form.label :alt, t('description') %>:<br/>
7
+ <%= form.text_field :alt %>
8
+ </p>
@@ -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
+ &nbsp;
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 %>