vinsol_spree_themes 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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