vinsol_spree_themes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +36 -0
  3. data/.ruby-version +1 -0
  4. data/Appraisals +16 -0
  5. data/Gemfile +9 -0
  6. data/Gemfile.lock +386 -0
  7. data/LICENSE +26 -0
  8. data/README.md +250 -0
  9. data/Rakefile +21 -0
  10. data/app/assets/javascripts/spree/backend/codemirror/codemirror.js +9351 -0
  11. data/app/assets/javascripts/spree/backend/codemirror/css-hint.js +60 -0
  12. data/app/assets/javascripts/spree/backend/codemirror/fullscreen.js +41 -0
  13. data/app/assets/javascripts/spree/backend/codemirror/html-hint.js +348 -0
  14. data/app/assets/javascripts/spree/backend/codemirror/javascript-hint.js +155 -0
  15. data/app/assets/javascripts/spree/backend/codemirror/javascript.js +813 -0
  16. data/app/assets/javascripts/spree/backend/codemirror/markdown.js +796 -0
  17. data/app/assets/javascripts/spree/backend/codemirror/ruby.js +295 -0
  18. data/app/assets/javascripts/spree/backend/codemirror/show-hint.js +438 -0
  19. data/app/assets/javascripts/spree/backend/editor.js +11 -0
  20. data/app/assets/javascripts/spree/backend/jquery.treemenu.js +87 -0
  21. data/app/assets/javascripts/spree/backend/main.js +26 -0
  22. data/app/assets/javascripts/spree/backend/spree_themes.js +2 -0
  23. data/app/assets/javascripts/spree/backend/template.js +51 -0
  24. data/app/assets/javascripts/spree/backend/theme.js +17 -0
  25. data/app/assets/javascripts/spree/frontend/spree_themes.js +2 -0
  26. data/app/assets/stylesheets/spree/backend/codemirror/codemirror.css +340 -0
  27. data/app/assets/stylesheets/spree/backend/codemirror/fullscreen.css +6 -0
  28. data/app/assets/stylesheets/spree/backend/codemirror/show-hint.css +36 -0
  29. data/app/assets/stylesheets/spree/backend/editor.css +7 -0
  30. data/app/assets/stylesheets/spree/backend/jquery.treemenu.css +8 -0
  31. data/app/assets/stylesheets/spree/backend/spree_themes.css +7 -0
  32. data/app/assets/stylesheets/spree/backend/style.css +239 -0
  33. data/app/assets/stylesheets/spree/backend/template_editor.css +39 -0
  34. data/app/assets/stylesheets/spree/frontend/spree_themes.css +6 -0
  35. data/app/assets/stylesheets/spree/frontend/theme_preview.css +9 -0
  36. data/app/controllers/spree/admin/themes_controller.rb +80 -0
  37. data/app/controllers/spree/admin/themes_preview_controller.rb +30 -0
  38. data/app/controllers/spree/admin/themes_templates_controller.rb +60 -0
  39. data/app/controllers/spree/fragment_cache_controller.rb +13 -0
  40. data/app/controllers/spree/store_controller_decorator.rb +37 -0
  41. data/app/helpers/spree/base_helper_decorator.rb +45 -0
  42. data/app/models/spree/theme.rb +153 -0
  43. data/app/models/spree/themes_template.rb +67 -0
  44. data/app/models/spree/themes_template/cache_resolver.rb +37 -0
  45. data/app/overrides/spree/add_preview_bar_to_store.html.rb +6 -0
  46. data/app/overrides/spree/add_theme_meta_details_to_store.html.rb +6 -0
  47. data/app/overrides/spree/admin/add_themes_tab.html.rb +6 -0
  48. data/app/services/assets_precompiler_service.rb +86 -0
  49. data/app/services/file_generator_service.rb +29 -0
  50. data/app/services/template_generator_service.rb +101 -0
  51. data/app/services/zip_file_builder.rb +87 -0
  52. data/app/services/zip_file_extractor.rb +52 -0
  53. data/app/views/spree/admin/shared/_theme_menu_button.html.erb +5 -0
  54. data/app/views/spree/admin/themes/_themes.html.erb +44 -0
  55. data/app/views/spree/admin/themes/_upload.html.erb +17 -0
  56. data/app/views/spree/admin/themes/index.html.erb +50 -0
  57. data/app/views/spree/admin/themes_templates/_editor.html.erb +29 -0
  58. data/app/views/spree/admin/themes_templates/_template.html.erb +12 -0
  59. data/app/views/spree/admin/themes_templates/edit.html.erb +13 -0
  60. data/app/views/spree/admin/themes_templates/edit.js.erb +1 -0
  61. data/app/views/spree/admin/themes_templates/index.html.erb +27 -0
  62. data/app/views/spree/admin/themes_templates/new.html.erb +56 -0
  63. data/app/views/spree/shared/_preview_bar.html.erb +7 -0
  64. data/app/views/spree/shared/_theme_metadata.html.erb +9 -0
  65. data/bin/rails +7 -0
  66. data/config/initializers/assets.rb +24 -0
  67. data/config/initializers/sprockets.rb +85 -0
  68. data/config/locales/en.yml +52 -0
  69. data/config/routes.rb +22 -0
  70. data/db/migrate/20170628072452_vinsol_spree_themes_create_themes_and_themes_templates_table.rb +24 -0
  71. data/gemfiles/spree_3_1.gemfile +8 -0
  72. data/gemfiles/spree_3_2.gemfile +9 -0
  73. data/gemfiles/spree_master.gemfile +9 -0
  74. data/lib/generators/themes/default.zip +0 -0
  75. data/lib/generators/vinsol_spree_themes/install/install_generator.rb +50 -0
  76. data/lib/tasks/sync_templates.rake +47 -0
  77. data/lib/vinsol_spree_themes.rb +3 -0
  78. data/lib/vinsol_spree_themes/engine.rb +22 -0
  79. data/lib/vinsol_spree_themes/factories.rb +6 -0
  80. data/lib/vinsol_spree_themes/version.rb +18 -0
  81. data/spec/spec_helper.rb +93 -0
  82. data/vinsol_spree_themes.gemspec +43 -0
  83. metadata +374 -0
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module Admin
3
+ class ThemesPreviewController < Spree::Admin::BaseController
4
+
5
+ before_action :load_theme, only: [:show, :destroy]
6
+
7
+ def show
8
+ cookies[:preview] = @theme.name
9
+ @theme.open_preview
10
+ redirect_to root_path(theme: @theme.id, mode: 'preview')
11
+ end
12
+
13
+ def destroy
14
+ cookies.delete(:preview)
15
+ @theme.close_preview
16
+ redirect_to admin_themes_path
17
+ end
18
+
19
+ private
20
+
21
+ def load_theme
22
+ @theme = Spree::Theme.find_by(id: params[:theme_id])
23
+ unless @theme
24
+ redirect_to admin_themes_path
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ module Spree
2
+ module Admin
3
+ class ThemesTemplatesController < Spree::Admin::BaseController
4
+
5
+ before_action :load_theme, only: [:index, :new, :create, :edit, :update]
6
+ before_action :load_template, only: [:edit, :update]
7
+
8
+ def index
9
+ end
10
+
11
+ def new
12
+ @template = @theme.themes_templates.build
13
+ end
14
+
15
+ def create
16
+ @template = @theme.themes_templates.build(new_template_params)
17
+ @template.created_by_admin = true
18
+
19
+ if @template.save
20
+ redirect_to admin_theme_templates_path(@theme)
21
+ else
22
+ render :new
23
+ end
24
+ end
25
+
26
+ def edit
27
+ end
28
+
29
+ def update
30
+ @template.update(template_params)
31
+ render :edit
32
+ end
33
+
34
+ private
35
+
36
+ def load_theme
37
+ @theme = Spree::Theme.find_by(id: params[:theme_id])
38
+ unless @theme
39
+ redirect_to admin_themes_path
40
+ end
41
+ end
42
+
43
+ def load_template
44
+ @template = @theme.templates.find_by(id: params[:id])
45
+ unless @template
46
+ redirect_to admin_theme_templates_path(@theme)
47
+ end
48
+ end
49
+
50
+ def template_params
51
+ params.require(:themes_template).permit(:body)
52
+ end
53
+
54
+ def new_template_params
55
+ params.require(:themes_template).permit(:body, :name, :path)
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ class FragmentCacheController < ActionController::Base
3
+
4
+ CACHE_PATH_REGEX = /\A(views\/spree_cache)/
5
+ CACHE_KEY = 'spree_cache'
6
+
7
+ def clear_cache
8
+ Rails.logger.info 'Expiring fragment caching for template...'
9
+ expire_fragment(CACHE_PATH_REGEX)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ Spree::StoreController::THEME_VIEW_LOAD_PATH = File.join(Spree::Theme::CURRENT_THEME_PATH, 'views')
2
+
3
+ module Spree
4
+ StoreController.class_eval do
5
+
6
+ fragment_cache_key Spree::ThemesTemplate::CacheResolver::FRAGMENT_CACHE_KEY
7
+
8
+ before_action :set_view_path
9
+
10
+ before_action :set_preview_theme, if: [:preview_mode?, :preview_theme]
11
+
12
+ private
13
+
14
+ def set_preview_theme
15
+ params.merge!({ mode: 'preview', theme: preview_theme.id })
16
+ end
17
+
18
+ def preview_mode?
19
+ cookies[:preview].present?
20
+ end
21
+ helper_method :preview_mode?
22
+
23
+ def set_view_path
24
+ path = preview_mode? ? theme_preview_path : Spree::StoreController::THEME_VIEW_LOAD_PATH
25
+ prepend_view_path path
26
+ end
27
+
28
+ def theme_preview_path
29
+ File.join(Spree::Theme::THEMES_PATH, cookies[:preview], 'views')
30
+ end
31
+
32
+ def preview_theme
33
+ @preview_theme ||= Spree::Theme.find_by(name: cookies[:preview])
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ Spree::BaseHelper.class_eval do
2
+
3
+ DEFAULT_THEME_THUMBNAIL_NAME = 'snapshot.png'
4
+ INVALID_DIRECTORIES = ['.', '..', 'precompiled_assets', 'images', 'fonts']
5
+
6
+ def snapshot_path(theme)
7
+ File.join('/vinsol_spree_themes', theme.name, DEFAULT_THEME_THUMBNAIL_NAME)
8
+ end
9
+
10
+ def sorted_themes(themes)
11
+ themes.sort { |a, b| Spree::Theme::STATES.index(b.state) <=> Spree::Theme::STATES.index(a.state) }
12
+ end
13
+
14
+ # FIX_ME_PG:- This helper method needs to be defined here as it is used by spree_auth_devise extension templates.
15
+ # spree_auth_devise not able to load the helper method from the store_controller.
16
+ def preview_mode?
17
+ cookies[:preview].present?
18
+ end
19
+
20
+ # FIX_ME_PG:- Optimize the logic for building the directory files tree strucutre.
21
+ def display_filetree_structure(theme, path, name=nil)
22
+ html_string ||= ''
23
+ html_string << '<ul>'
24
+
25
+ Dir.foreach(path) do |entry|
26
+ next if INVALID_DIRECTORIES.include?(entry)
27
+ full_path = File.join(path, entry)
28
+
29
+ if File.directory?(full_path)
30
+ html_string << "<li><a href='javascript:void(0)'>#{ entry }</a>"
31
+ html_string << "#{ display_filetree_structure(theme, full_path, entry) }"
32
+ else
33
+ # FIX_ME_PG:- Currently need to load the template for every filepath on basis of path and name.
34
+ template_path = File.dirname(full_path.sub(Rails.root.to_s + '/', ''))
35
+ template = theme.templates.find_by(path: template_path, name: entry)
36
+ next unless template
37
+ html_string << "<li>#{ link_to entry, edit_admin_theme_template_path(theme, template), remote: true, class: 'file-link' }</li>"
38
+ end
39
+ end
40
+ html_string << '</ul>'
41
+
42
+ return html_string
43
+ end
44
+
45
+ end
@@ -0,0 +1,153 @@
1
+ module Spree
2
+ class Theme < Spree::Base
3
+
4
+ DEFAULT_NAME = %w(default)
5
+ DEFAULT_STATE = 'drafted'
6
+ TEMPLATE_FILE_CONTENT_TYPE = 'application/zip'
7
+ STATES = %w(drafted compiled published)
8
+ THEMES_PATH = File.join(Rails.root, 'public', 'vinsol_spree_themes')
9
+ CURRENT_THEME_PATH = File.join(THEMES_PATH, 'current')
10
+ ASSET_CACHE_PATH = File.join(Rails.root, 'tmp', 'cache')
11
+
12
+ has_attached_file :template_file, storage: :filesystem,
13
+ path: 'public/system/spree/themes/:filename'
14
+
15
+ ## VALIDATIONS ##
16
+ validates_attachment :template_file, presence: true,
17
+ content_type: { content_type: TEMPLATE_FILE_CONTENT_TYPE }
18
+ do_not_validate_attachment_file_type :template_file
19
+
20
+ validates :name, presence: true,
21
+ uniqueness: { case_sensitive: false }
22
+ validates :state, inclusion: { in: STATES }
23
+
24
+ # FIX_ME_PG:- check for atleast one published theme.
25
+ # validate :ensure_atleast_one_published_theme, if: :state_changed?, unless: :published?
26
+
27
+ ## ASSOCIATIONS ##
28
+ has_many :themes_templates, dependent: :destroy
29
+
30
+ ## CALLBACKS ##
31
+ before_validation :set_name, if: :template_file?
32
+ before_validation :set_state, unless: :state?
33
+ after_commit :extract_template_zip_file, on: :create
34
+ # before_destroy :ensure_not_published, prepend: true
35
+ after_destroy :delete_from_file_system
36
+
37
+ # FIX_ME_PG:- Need to have default state to compiled when uploading theme. Set state after zip file extraction.
38
+ # after_create :set_state_to_compile
39
+
40
+ ## SCOPES ##
41
+ scope :published, -> { where(state: 'published') }
42
+ scope :default, -> { where(name: DEFAULT_NAME) }
43
+
44
+ alias_method :templates, :themes_templates
45
+
46
+ self.whitelisted_ransackable_attributes = %w( name state )
47
+
48
+ ## STATE MACHINES ##
49
+ state_machine initial: :drafted do
50
+ before_transition drafted: :compiled do |theme, transition|
51
+ begin
52
+ theme.assets_precompile
53
+ theme.update_cache_timestamp
54
+ rescue Exception => e
55
+ theme.errors.add(:base, e)
56
+ end
57
+ end
58
+
59
+ before_transition compiled: :published do |theme, transition|
60
+ begin
61
+ theme.remove_current_theme
62
+ theme.apply_new_theme
63
+ theme.update_cache_timestamp
64
+ rescue Exception => e
65
+ theme.errors.add(:base, e)
66
+ end
67
+ end
68
+
69
+ event :draft do
70
+ transition from: [:published, :compiled], to: :drafted
71
+ end
72
+
73
+ event :compile do
74
+ transition from: :drafted, to: :compiled
75
+ end
76
+
77
+ event :publish do
78
+ transition from: [:compiled, :drafted], to: :published
79
+ end
80
+ end
81
+
82
+ def assets_precompile
83
+ AssetsPrecompilerService.new(self).minify
84
+ end
85
+
86
+ def remove_current_theme
87
+ Spree::Theme.published.each(&:draft)
88
+ File.delete(CURRENT_THEME_PATH) if File.exist?(CURRENT_THEME_PATH)
89
+ end
90
+
91
+ def apply_new_theme
92
+ source_path = File.join(THEMES_PATH, name)
93
+ FileUtils.ln_sf(source_path, CURRENT_THEME_PATH)
94
+ AssetsPrecompilerService.new(self).copy_assets
95
+ end
96
+
97
+ def open_preview
98
+ precompile_assets = AssetsPrecompilerService.new(self)
99
+ precompile_assets.minify
100
+ precompile_assets.copy_preview_assets
101
+ remove_cache
102
+ # update_cache_timestamp
103
+ end
104
+
105
+ def close_preview
106
+ remove_cache
107
+ # update_cache_timestamp
108
+ end
109
+
110
+ def update_cache_timestamp
111
+ Rails.cache.write(Spree::ThemesTemplate::CacheResolver.cache_key, Time.current)
112
+ end
113
+
114
+ private
115
+
116
+ # def ensure_atleast_one_published_theme
117
+ # errors.add(:base, Spree.t('models.theme.minimum_active_error')) unless Spree::Theme.published.one?
118
+ # end
119
+
120
+ def remove_cache
121
+ FileUtils.remove_dir(ASSET_CACHE_PATH) if File.exists?(ASSET_CACHE_PATH)
122
+ end
123
+
124
+ def set_name
125
+ self.name = File.basename(template_file_file_name, File.extname(template_file_file_name))
126
+ end
127
+
128
+ def set_state
129
+ self.state = DEFAULT_STATE
130
+ end
131
+
132
+ def extract_template_zip_file
133
+ ZipFileExtractor.new(template_file.path, self).extract
134
+ end
135
+
136
+ def delete_from_file_system
137
+ source_dir = File.join(THEMES_PATH, name)
138
+ FileUtils.remove_dir(source_dir)
139
+ end
140
+
141
+ def ensure_not_published
142
+ if published?
143
+ errors.add(:base, Spree.t('models.theme.no_destory_error'))
144
+ throw(:abort)
145
+ end
146
+ end
147
+
148
+ # def set_state_to_compile
149
+ # self.compile!
150
+ # end
151
+
152
+ end
153
+ end
@@ -0,0 +1,67 @@
1
+ module Spree
2
+ class ThemesTemplate < Spree::Base
3
+
4
+ DEFAULT_LOCALE = 'en'
5
+ DEFAULT_PATH = File.join('public', 'vinsol_spree_themes')
6
+ ASSETS_FILE_EXTENSIONS = ['.js', '.css']
7
+
8
+ # this attr attribute is used when templates are created from admin end.
9
+ attr_accessor :created_by_admin
10
+
11
+ ## VALIDATIONS ##
12
+ validates :path, presence: true
13
+ validates :format, inclusion: Mime::SET.symbols.map(&:to_s),
14
+ allow_nil: true
15
+ validates :locale, inclusion: I18n.available_locales.map(&:to_s)
16
+ validates :handler, inclusion: ActionView::Template::Handlers.extensions.map(&:to_s),
17
+ allow_nil: true
18
+
19
+ ## ASSOCIATIONS ##
20
+ belongs_to :theme
21
+
22
+ ## CALLBACKS ##
23
+ before_validation :set_default_locale, unless: :locale?
24
+ before_create :set_public_path, if: :created_by_admin
25
+ after_save :update_cache_timestamp
26
+ after_save :update_public_file
27
+ after_save :precompile_assets, if: [:theme_published?, :assets_file?]
28
+
29
+ ## DELEGATES ##
30
+ delegate :name, to: :theme, prefix: true
31
+
32
+ self.whitelisted_ransackable_attributes = %w( name path format handler )
33
+
34
+ private
35
+
36
+ def update_cache_timestamp
37
+ Rails.cache.write(Spree::ThemesTemplate::CacheResolver.cache_key, Time.current)
38
+ end
39
+
40
+ def set_default_locale
41
+ self.locale = DEFAULT_LOCALE
42
+ end
43
+
44
+ def set_public_path
45
+ self.path = File.join(DEFAULT_PATH, theme_name, path)
46
+ end
47
+
48
+ def update_public_file
49
+ FileGeneratorService.create(self)
50
+ end
51
+
52
+ def precompile_assets
53
+ assets_compiler = AssetsPrecompilerService.new(theme)
54
+ assets_compiler.minify
55
+ assets_compiler.copy_assets
56
+ end
57
+
58
+ def theme_published?
59
+ theme.published?
60
+ end
61
+
62
+ def assets_file?
63
+ ASSETS_FILE_EXTENSIONS.include? File.extname(name)
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ module Spree
2
+ class ThemesTemplate::CacheResolver < ActionView::FileSystemResolver
3
+
4
+ FRAGMENT_CACHE_PATH_REGEX = /\A(views\/spree_cache)/
5
+ FRAGMENT_CACHE_KEY = 'spree_cache'
6
+
7
+ def initialize(path)
8
+ super(path)
9
+ end
10
+
11
+ def find_all(*args)
12
+ clear_cache_if_necessary
13
+ super
14
+ end
15
+
16
+ def self.cache_key
17
+ ActiveSupport::Cache.expand_cache_key('updated_at', 'loaded_templates')
18
+ end
19
+
20
+ private
21
+
22
+ def clear_cache_if_necessary
23
+ last_updated = Rails.cache.fetch(Spree::ThemesTemplate::CacheResolver.cache_key) { Time.current }
24
+
25
+ if @cache_last_updated.nil? || @cache_last_updated < last_updated
26
+ Rails.logger.info 'Expiring cache and reloading new template content....'
27
+ # expiring fragment caching used in spree views
28
+ ActionController::Base.new().expire_fragment(FRAGMENT_CACHE_PATH_REGEX)
29
+ # clearing template caching.
30
+ clear_cache
31
+
32
+ @cache_last_updated = last_updated
33
+ end
34
+ end
35
+
36
+ end
37
+ end