├── .editorconfig
├── .gitignore
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── VERSION
├── app
├── assets
│ ├── images
│ │ └── bhf
│ │ │ ├── ajax_loader.gif
│ │ │ ├── bg.png
│ │ │ ├── logo_bhf.svg
│ │ │ ├── mooeditable-toolbarbuttons-tango.png
│ │ │ ├── pictos.png
│ │ │ ├── pictos_2x.png
│ │ │ ├── small_ajax_loader.gif
│ │ │ ├── small_ajax_loader_h.gif
│ │ │ └── wmd-buttons.png
│ ├── javascripts
│ │ └── bhf
│ │ │ ├── MooTools-More-1.6.0.js
│ │ │ ├── application.js
│ │ │ ├── classes
│ │ │ ├── Ajaxify.js
│ │ │ ├── ArrayFields.js
│ │ │ ├── FormHelper.js
│ │ │ ├── MooEditable.js
│ │ │ ├── MultipleFields.js
│ │ │ ├── Picker.js
│ │ │ ├── Picker_Attach.js
│ │ │ ├── Picker_Date.js
│ │ │ ├── PlatformHelper.js
│ │ │ ├── QuickEdit.js
│ │ │ ├── QuickEditStack.js
│ │ │ ├── Request_bhf.js
│ │ │ ├── Setlatlng.js
│ │ │ ├── showdown.js
│ │ │ └── wmd.js
│ │ │ ├── locales
│ │ │ ├── Locale.de-DE.js
│ │ │ ├── Locale.en-US.js
│ │ │ └── Locale.pt-PT.js
│ │ │ ├── mootools_ujs.js
│ │ │ └── trix.js
│ └── stylesheets
│ │ └── bhf
│ │ ├── MooEditable.scss
│ │ ├── application.sass
│ │ ├── functions.sass
│ │ ├── reset.sass
│ │ ├── trix.scss
│ │ └── typo.scss
├── controllers
│ └── bhf
│ │ ├── application_controller.rb
│ │ ├── embed_entries_controller.rb
│ │ ├── entries_controller.rb
│ │ └── pages_controller.rb
├── helpers
│ └── bhf
│ │ ├── application_helper.rb
│ │ ├── entries_helper.rb
│ │ ├── frontend_helper.rb
│ │ └── pages_helper.rb
└── views
│ ├── bhf
│ ├── _footer.html.haml
│ ├── _user.html.haml
│ ├── application
│ │ └── index.html.haml
│ ├── entries
│ │ ├── _form.html.haml
│ │ ├── _validation_errors.html.haml
│ │ ├── edit.html.haml
│ │ ├── new.html.haml
│ │ └── show.html.haml
│ ├── form
│ │ ├── belongs_to
│ │ │ ├── _radio.html.haml
│ │ │ ├── _select.html.haml
│ │ │ └── _static.html.haml
│ │ ├── column
│ │ │ ├── _activestorage_attachment.html.haml
│ │ │ ├── _activestorage_attachments.html.haml
│ │ │ ├── _array.html.haml
│ │ │ ├── _attachment_presenter.html.haml
│ │ │ ├── _boolean.html.haml
│ │ │ ├── _date.html.haml
│ │ │ ├── _hash.html.haml
│ │ │ ├── _hidden.html.haml
│ │ │ ├── _image_file.html.haml
│ │ │ ├── _mappin.html.haml
│ │ │ ├── _markdown.html.haml
│ │ │ ├── _multiple_fields.html.haml
│ │ │ ├── _number.html.haml
│ │ │ ├── _password.html.haml
│ │ │ ├── _rich_text.html.haml
│ │ │ ├── _static.html.haml
│ │ │ ├── _string.html.haml
│ │ │ ├── _text.html.haml
│ │ │ ├── _type.html.haml
│ │ │ └── _wysiwyg.html.haml
│ │ ├── embeds_many
│ │ │ └── _static.html.haml
│ │ ├── embeds_one
│ │ │ └── _static.html.haml
│ │ ├── has_and_belongs_to_many
│ │ │ ├── _check_box.html.haml
│ │ │ └── _static.html.haml
│ │ ├── has_many
│ │ │ ├── _check_box.html.haml
│ │ │ └── _static.html.haml
│ │ └── has_one
│ │ │ ├── _select.html.haml
│ │ │ └── _static.html.haml
│ ├── helper
│ │ ├── _definition_item.html.haml
│ │ ├── _field_errors.html.haml
│ │ ├── _flash.html.haml
│ │ ├── _frontend_edit.html.haml
│ │ ├── _info.html.haml
│ │ ├── _node.html.haml
│ │ └── _reflection_node.html.haml
│ ├── pages
│ │ ├── _platform.html.haml
│ │ ├── _search.html.haml
│ │ └── show.html.haml
│ └── table
│ │ ├── belongs_to
│ │ └── _default.html.haml
│ │ ├── column
│ │ ├── _array.html.haml
│ │ ├── _boolean.html.haml
│ │ ├── _carrierwave.html.haml
│ │ ├── _date.html.haml
│ │ ├── _empty.html.haml
│ │ ├── _extern_link.html.haml
│ │ ├── _file.html.haml
│ │ ├── _hash.html.haml
│ │ ├── _image.html.haml
│ │ ├── _number.html.haml
│ │ ├── _primary_key.html.haml
│ │ ├── _string.html.haml
│ │ ├── _text.html.haml
│ │ ├── _thumbnail.html.haml
│ │ ├── _toggle.html.haml
│ │ └── _type.html.haml
│ │ ├── embeds_many
│ │ └── _default.html.haml
│ │ ├── embeds_one
│ │ └── _default.html.haml
│ │ ├── has_and_belongs_to_many
│ │ └── _default.html.haml
│ │ ├── has_many
│ │ └── _default.html.haml
│ │ └── has_one
│ │ └── _default.html.haml
│ ├── kaminari
│ └── bhf
│ │ ├── _gap.html.haml
│ │ ├── _next_page.html.haml
│ │ ├── _page.html.haml
│ │ ├── _paginator.html.haml
│ │ └── _prev_page.html.haml
│ └── layouts
│ └── bhf
│ ├── application.haml
│ └── quick_edit.haml
├── bhf.gemspec
├── config
├── locales
│ ├── de.yml
│ ├── en.yml
│ └── pt.yml
└── routes.rb
├── lib
├── bhf.rb
├── bhf
│ ├── action_view
│ │ ├── form_builder.rb
│ │ └── form_options.rb
│ ├── active_record
│ │ └── base.rb
│ ├── controller
│ │ └── extension.rb
│ ├── mongoid
│ │ └── document.rb
│ ├── platform
│ │ ├── attribute
│ │ │ ├── abstract.rb
│ │ │ ├── column.rb
│ │ │ └── reflection.rb
│ │ ├── base.rb
│ │ └── pagination.rb
│ └── settings
│ │ ├── base.rb
│ │ ├── platform.rb
│ │ └── yaml_parser.rb
└── rails
│ └── generators
│ └── bhf
│ └── templates
│ └── initializer.rb
└── test
├── database.yml
└── test_helper.rb
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.js]
13 | indent_style = tab
14 |
15 | [*.{md,haml}]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .sass-cache/
2 | pkg/
3 | doc/
4 | .yardoc/
5 | .bundle/
6 | **/*.sw*
7 | **/*~
8 | .ruby-version
9 | .ruby-gemset
10 | .rvmrc
11 | node_modules
12 | /public/packs
13 | /public/packs-test
14 | /public/bhf-packs
15 | /public/bhf-packs-test
16 | /tmp/*
17 | bhf-*.gem
18 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 |
3 | gem 'rails', '>= 5.2.2.1'
4 | gem 'sass-rails', '>= 6'
5 | gem 'turbolinks', '>= 2'
6 | gem 'kaminari', '>= 0'
7 | gem 'haml-rails', '>= 0'
8 |
9 |
10 | group :development do
11 | gem 'shoulda', '>= 3'
12 | gem 'rdoc', '>= 3'
13 | gem 'bundler', '>= 1'
14 | gem 'jeweler', '>= 2'
15 | gem 'simplecov', '>= 0'
16 | end
17 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | actioncable (6.0.2.1)
5 | actionpack (= 6.0.2.1)
6 | nio4r (~> 2.0)
7 | websocket-driver (>= 0.6.1)
8 | actionmailbox (6.0.2.1)
9 | actionpack (= 6.0.2.1)
10 | activejob (= 6.0.2.1)
11 | activerecord (= 6.0.2.1)
12 | activestorage (= 6.0.2.1)
13 | activesupport (= 6.0.2.1)
14 | mail (>= 2.7.1)
15 | actionmailer (6.0.2.1)
16 | actionpack (= 6.0.2.1)
17 | actionview (= 6.0.2.1)
18 | activejob (= 6.0.2.1)
19 | mail (~> 2.5, >= 2.5.4)
20 | rails-dom-testing (~> 2.0)
21 | actionpack (6.0.2.1)
22 | actionview (= 6.0.2.1)
23 | activesupport (= 6.0.2.1)
24 | rack (~> 2.0, >= 2.0.8)
25 | rack-test (>= 0.6.3)
26 | rails-dom-testing (~> 2.0)
27 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
28 | actiontext (6.0.2.1)
29 | actionpack (= 6.0.2.1)
30 | activerecord (= 6.0.2.1)
31 | activestorage (= 6.0.2.1)
32 | activesupport (= 6.0.2.1)
33 | nokogiri (>= 1.8.5)
34 | actionview (6.0.2.1)
35 | activesupport (= 6.0.2.1)
36 | builder (~> 3.1)
37 | erubi (~> 1.4)
38 | rails-dom-testing (~> 2.0)
39 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
40 | activejob (6.0.2.1)
41 | activesupport (= 6.0.2.1)
42 | globalid (>= 0.3.6)
43 | activemodel (6.0.2.1)
44 | activesupport (= 6.0.2.1)
45 | activerecord (6.0.2.1)
46 | activemodel (= 6.0.2.1)
47 | activesupport (= 6.0.2.1)
48 | activestorage (6.0.2.1)
49 | actionpack (= 6.0.2.1)
50 | activejob (= 6.0.2.1)
51 | activerecord (= 6.0.2.1)
52 | marcel (~> 0.3.1)
53 | activesupport (6.0.2.1)
54 | concurrent-ruby (~> 1.0, >= 1.0.2)
55 | i18n (>= 0.7, < 2)
56 | minitest (~> 5.1)
57 | tzinfo (~> 1.1)
58 | zeitwerk (~> 2.2)
59 | addressable (2.4.0)
60 | builder (3.2.4)
61 | concurrent-ruby (1.1.6)
62 | crass (1.0.6)
63 | descendants_tracker (0.0.4)
64 | thread_safe (~> 0.3, >= 0.3.1)
65 | docile (1.3.2)
66 | erubi (1.9.0)
67 | erubis (2.7.0)
68 | faraday (0.9.2)
69 | multipart-post (>= 1.2, < 3)
70 | ffi (1.11.3)
71 | git (1.5.0)
72 | github_api (0.16.0)
73 | addressable (~> 2.4.0)
74 | descendants_tracker (~> 0.0.4)
75 | faraday (~> 0.8, < 0.10)
76 | hashie (>= 3.4)
77 | mime-types (>= 1.16, < 3.0)
78 | oauth2 (~> 1.0)
79 | globalid (0.4.2)
80 | activesupport (>= 4.2.0)
81 | haml (5.1.2)
82 | temple (>= 0.8.0)
83 | tilt
84 | haml-rails (2.0.1)
85 | actionpack (>= 5.1)
86 | activesupport (>= 5.1)
87 | haml (>= 4.0.6, < 6.0)
88 | html2haml (>= 1.0.1)
89 | railties (>= 5.1)
90 | hashie (4.0.0)
91 | highline (2.0.3)
92 | html2haml (2.2.0)
93 | erubis (~> 2.7.0)
94 | haml (>= 4.0, < 6)
95 | nokogiri (>= 1.6.0)
96 | ruby_parser (~> 3.5)
97 | i18n (1.8.3)
98 | concurrent-ruby (~> 1.0)
99 | jeweler (2.3.9)
100 | builder
101 | bundler
102 | git (>= 1.2.5)
103 | github_api (~> 0.16.0)
104 | highline (>= 1.6.15)
105 | nokogiri (>= 1.5.10)
106 | psych
107 | rake
108 | rdoc
109 | semver2
110 | json (2.3.0)
111 | jwt (2.2.1)
112 | kaminari (1.2.1)
113 | activesupport (>= 4.1.0)
114 | kaminari-actionview (= 1.2.1)
115 | kaminari-activerecord (= 1.2.1)
116 | kaminari-core (= 1.2.1)
117 | kaminari-actionview (1.2.1)
118 | actionview
119 | kaminari-core (= 1.2.1)
120 | kaminari-activerecord (1.2.1)
121 | activerecord
122 | kaminari-core (= 1.2.1)
123 | kaminari-core (1.2.1)
124 | loofah (2.6.0)
125 | crass (~> 1.0.2)
126 | nokogiri (>= 1.5.9)
127 | mail (2.7.1)
128 | mini_mime (>= 0.1.1)
129 | marcel (0.3.3)
130 | mimemagic (~> 0.3.2)
131 | method_source (0.9.2)
132 | mime-types (2.99.3)
133 | mimemagic (0.3.10)
134 | nokogiri (~> 1)
135 | rake
136 | mini_mime (1.0.2)
137 | mini_portile2 (2.8.0)
138 | minitest (5.14.1)
139 | multi_json (1.14.1)
140 | multi_xml (0.6.0)
141 | multipart-post (2.1.1)
142 | nio4r (2.5.2)
143 | nokogiri (1.13.4)
144 | mini_portile2 (~> 2.8.0)
145 | racc (~> 1.4)
146 | oauth2 (1.4.2)
147 | faraday (>= 0.8, < 2.0)
148 | jwt (>= 1.0, < 3.0)
149 | multi_json (~> 1.3)
150 | multi_xml (~> 0.5)
151 | rack (>= 1.2, < 3)
152 | psych (3.1.0)
153 | racc (1.6.0)
154 | rack (2.2.3)
155 | rack-test (1.1.0)
156 | rack (>= 1.0, < 3)
157 | rails (6.0.2.1)
158 | actioncable (= 6.0.2.1)
159 | actionmailbox (= 6.0.2.1)
160 | actionmailer (= 6.0.2.1)
161 | actionpack (= 6.0.2.1)
162 | actiontext (= 6.0.2.1)
163 | actionview (= 6.0.2.1)
164 | activejob (= 6.0.2.1)
165 | activemodel (= 6.0.2.1)
166 | activerecord (= 6.0.2.1)
167 | activestorage (= 6.0.2.1)
168 | activesupport (= 6.0.2.1)
169 | bundler (>= 1.3.0)
170 | railties (= 6.0.2.1)
171 | sprockets-rails (>= 2.0.0)
172 | rails-dom-testing (2.0.3)
173 | activesupport (>= 4.2.0)
174 | nokogiri (>= 1.6)
175 | rails-html-sanitizer (1.3.0)
176 | loofah (~> 2.3)
177 | railties (6.0.2.1)
178 | actionpack (= 6.0.2.1)
179 | activesupport (= 6.0.2.1)
180 | method_source
181 | rake (>= 0.8.7)
182 | thor (>= 0.20.3, < 2.0)
183 | rake (13.0.1)
184 | rdoc (6.3.1)
185 | ruby_parser (3.14.1)
186 | sexp_processor (~> 4.9)
187 | sass-rails (6.0.0)
188 | sassc-rails (~> 2.1, >= 2.1.1)
189 | sassc (2.2.1)
190 | ffi (~> 1.9)
191 | sassc-rails (2.1.2)
192 | railties (>= 4.0.0)
193 | sassc (>= 2.0)
194 | sprockets (> 3.0)
195 | sprockets-rails
196 | tilt
197 | semver2 (3.4.2)
198 | sexp_processor (4.13.0)
199 | shoulda (3.6.0)
200 | shoulda-context (~> 1.0, >= 1.0.1)
201 | shoulda-matchers (~> 3.0)
202 | shoulda-context (1.2.2)
203 | shoulda-matchers (3.1.3)
204 | activesupport (>= 4.0.0)
205 | simplecov (0.17.1)
206 | docile (~> 1.1)
207 | json (>= 1.8, < 3)
208 | simplecov-html (~> 0.10.0)
209 | simplecov-html (0.10.2)
210 | sprockets (4.0.0)
211 | concurrent-ruby (~> 1.0)
212 | rack (> 1, < 3)
213 | sprockets-rails (3.2.1)
214 | actionpack (>= 4.0)
215 | activesupport (>= 4.0)
216 | sprockets (>= 3.0.0)
217 | temple (0.8.2)
218 | thor (1.0.1)
219 | thread_safe (0.3.6)
220 | tilt (2.0.10)
221 | turbolinks (5.2.1)
222 | turbolinks-source (~> 5.2)
223 | turbolinks-source (5.2.0)
224 | tzinfo (1.2.7)
225 | thread_safe (~> 0.1)
226 | websocket-driver (0.7.1)
227 | websocket-extensions (>= 0.1.0)
228 | websocket-extensions (0.1.5)
229 | zeitwerk (2.4.0)
230 |
231 | PLATFORMS
232 | ruby
233 |
234 | DEPENDENCIES
235 | bundler (>= 1)
236 | haml-rails
237 | jeweler (>= 2)
238 | kaminari
239 | rails (>= 5.2.2.1)
240 | rdoc (>= 3)
241 | sass-rails (>= 6)
242 | shoulda (>= 3)
243 | simplecov
244 | turbolinks (>= 2)
245 |
246 | BUNDLED WITH
247 | 1.17.3
248 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Anton Pawlik
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bhf
2 |
3 | ## [Guide and Documentation](https://antpaw.github.io/bhf/)
4 |
5 | ## Contributing to bhf
6 |
7 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9 | * Fork the project.
10 | * Start a feature/bugfix branch.
11 | * Commit and push until you are happy with your contribution.
12 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
13 |
14 |
15 | ## Roadmap
16 |
17 | * Adding unit tests and integration tests
18 | * Adding bulk edit for the table view (aka platform)
19 | * Manage relations with help of autocomplete inputs
20 | * Move from MooTools to jQuery
21 |
22 | ## Support
23 |
24 |

25 |
26 |
27 | ## Copyright
28 |
29 | Copyright (c) 2014 Anton Pawlik. See LICENSE.txt for
30 | further details.
31 |
32 |
33 | ### Build
34 |
35 | gem install juwelier
36 | rake gemspec:generate
37 | git commit -m 'Regenerate gemspec for version 1.0.0.beta11' -a
38 | gem build bhf.gemspec
39 | gem push bhf-1.0.0.beta11.gem
40 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | require 'rubygems'
4 | require 'bundler'
5 | begin
6 | Bundler.setup(:default, :development)
7 | rescue Bundler::BundlerError => e
8 | $stderr.puts e.message
9 | $stderr.puts "Run `bundle install` to install missing gems"
10 | exit e.status_code
11 | end
12 | require 'rake'
13 |
14 | require 'jeweler'
15 | Jeweler::Tasks.new do |gem|
16 | gem.name = 'bhf'
17 | gem.homepage = 'https://antpaw.github.io/bhf'
18 | gem.licenses = 'MIT'
19 | gem.summary = 'A simple to use Rails-Engine-Gem that offers an admin interface for trusted user.'
20 | gem.description = 'A simple to use Rails-Engine-Gem that offers an admin interface for trusted user. Easy integratable and highly configurable and agnostic. Works with ActiveRecord and Mongoid.'
21 | gem.email = 'anton.pawlik@gmail.com'
22 | gem.authors = ['Anton Pawlik']
23 | end
24 | Jeweler::RubygemsDotOrgTasks.new
25 |
26 | require 'rake/testtask'
27 | Rake::TestTask.new(:test) do |test|
28 | test.libs << 'lib' << 'test'
29 | test.pattern = 'test/**/test_*.rb'
30 | test.verbose = true
31 | end
32 |
33 | desc "Code coverage detail"
34 | task :simplecov do
35 | ENV['COVERAGE'] = "true"
36 | Rake::Task['test'].execute
37 | end
38 |
39 | task :default => :test
40 |
41 | require 'rdoc/task'
42 | Rake::RDocTask.new do |rdoc|
43 | version = File.exist?('VERSION') ? File.read('VERSION') : ""
44 |
45 | rdoc.rdoc_dir = 'rdoc'
46 | rdoc.title = "bhf #{version}"
47 | rdoc.rdoc_files.include('README*')
48 | rdoc.rdoc_files.include('lib/**/*.rb')
49 | end
50 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 1.0.0.beta16
2 |
--------------------------------------------------------------------------------
/app/assets/images/bhf/ajax_loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/ajax_loader.gif
--------------------------------------------------------------------------------
/app/assets/images/bhf/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/bg.png
--------------------------------------------------------------------------------
/app/assets/images/bhf/logo_bhf.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/images/bhf/mooeditable-toolbarbuttons-tango.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/mooeditable-toolbarbuttons-tango.png
--------------------------------------------------------------------------------
/app/assets/images/bhf/pictos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/pictos.png
--------------------------------------------------------------------------------
/app/assets/images/bhf/pictos_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/pictos_2x.png
--------------------------------------------------------------------------------
/app/assets/images/bhf/small_ajax_loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/small_ajax_loader.gif
--------------------------------------------------------------------------------
/app/assets/images/bhf/small_ajax_loader_h.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/small_ajax_loader_h.gif
--------------------------------------------------------------------------------
/app/assets/images/bhf/wmd-buttons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antpaw/bhf/57d208c064568a0bea48063fd478b271ae0d2474/app/assets/images/bhf/wmd-buttons.png
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/application.js:
--------------------------------------------------------------------------------
1 | //= require turbolinks
2 | //= require activestorage
3 | //= require ./MooTools-More-1.6.0.js
4 | //= require ./mootools_ujs
5 | //= require ./trix
6 | //= require_tree ./locales/
7 | //= require_tree ./classes/
8 |
9 | (function(){
10 | var lang = document.html.get('lang').split('-')[0];
11 | lang = (lang === 'en') ? 'en-US' : lang.toLowerCase()+'-'+lang.toUpperCase();
12 | Locale.use(lang);
13 |
14 | var ajaxNote = new Ajaxify();
15 |
16 | var editStack = new QuickEditStack({
17 | permanentQuickEditEvents: {
18 | startRequest: function(){
19 | var linkElem = this.linkElem;
20 | var rowElem = this.linkElem.getParent('tr');
21 | if (rowElem) {
22 | linkElem = rowElem;
23 | }
24 | linkElem.addClass('live_edit');
25 | },
26 | closed: function(){
27 | var linkElem = this.linkElem;
28 | var rowElem = this.linkElem.getParent('tr');
29 | if (rowElem) {
30 | rowElem.addClass('animate');
31 | setTimeout(function(){
32 | rowElem.removeClass('animate');
33 | }, 600);
34 | linkElem = rowElem;
35 | }
36 | setTimeout(function(){
37 | linkElem.removeClass('live_edit');
38 | });
39 | }
40 | }
41 | });
42 |
43 | var triggerPlatformPagination = function(href, platform, callback){
44 | window.history.pushState({ turbolinks: true, url: href }, '', href);
45 | ajaxNote.loading();
46 | new Request.HTML({
47 | method: 'get',
48 | url: href,
49 | onFailure: function(){
50 | ajaxNote.failure();
51 | },
52 | onSuccess: function (a, b, html) {
53 | platform.innerHTML = html;
54 | if (callback) {
55 | callback.call();
56 | }
57 | window.fireEvent('platformUpdate', [platform.getParent()]);
58 | }
59 | }).send();
60 | };
61 |
62 | var updateElementsAfterQuickEditSuccess = function(eventNames, linkElem, json){
63 | var parent = linkElem.getParent('.quick_edit_block');
64 | var entry = linkElem.getParent('.quick_edit_entry');
65 | var entryTemplate, newEntryInjectArea, relation, parsedTemplate;
66 | if (parent) {
67 | entryTemplate = parent.getElement('.quick_edit_template');
68 | newEntryInjectArea = parent.getElement('.quick_edit_inject');
69 | relation = parent.getElement('.relation');
70 | }
71 | if ( ! entry) {
72 | entry = parent.getElement('.quick_edit_active');
73 | }
74 |
75 | if (entryTemplate) {
76 | entryTemplate = entryTemplate.clone();
77 | entryTemplate.getElements('.js_remove_disabled').each(function(elem){
78 | elem.removeProperty('disabled');
79 | });
80 | if (json) {
81 | parsedTemplate = entryTemplate.innerHTML.replace(new RegExp('%7Bobject_id%7D', 'g'), '{object_id}').substitute(json);
82 | }
83 | }
84 |
85 | if (eventNames.contains('successAndAdd')) {
86 | if (newEntryInjectArea && entryTemplate) {
87 | newEntryInjectArea.appendHTML(parsedTemplate);
88 | if (newEntryInjectArea) {
89 | newEntryInjectArea.fireEvent('quickEditEntryAdded');
90 | }
91 | }
92 |
93 | if (relation) {
94 | if (relation.getPrevious('.empty')) {
95 | relation.getPrevious('.empty').addClass('hide');
96 | }
97 | if (relation.hasClass('has_one') || relation.hasClass('embeds_one')) {
98 | relation.getNext('.js_add_field').addClass('hide');
99 | }
100 | }
101 | }
102 | if (eventNames.contains('successAndChange')) {
103 | if (entry && parsedTemplate) {
104 | entry.innerHTML = parsedTemplate;
105 | }
106 | }
107 | if (eventNames.contains('successAndRemove')) {
108 | if (entry) {
109 | entry.dispose();
110 | }
111 | if (newEntryInjectArea) {
112 | newEntryInjectArea.fireEvent('quickEditEntryRemoved');
113 | }
114 | }
115 | };
116 |
117 | var scrollContent = function(){
118 | editStack.bricksArray.each(function(quickEdit){
119 | var innerForm = quickEdit.holder.getElement('form');
120 | if ( ! innerForm) { return; }
121 | var scroll = document.body.scrollTop - 83;
122 | if (scroll + innerForm.getSize().y > document.body.clientHeight) { return; }
123 | quickEdit.holder.setStyle('padding-top', scroll);
124 | });
125 | };
126 |
127 |
128 | window.addEvent('bhfDomChunkReady', function(mainScope){
129 |
130 | var areaSelect = mainScope.getElement('#area_select');
131 | if (areaSelect) {
132 | areaSelect.addEvent('change', function(){
133 | location.href = this.value;
134 | });
135 | }
136 |
137 | var quickEditOptions;
138 |
139 | ajaxNote.setup();
140 |
141 | var jsForm = new FormHelper();
142 | mainScope.getElements('.js_bhf_form').each(function(form){
143 | jsForm.setup(form);
144 | });
145 |
146 | var sharedQuickEditOptions = {
147 | onFailure: function(){
148 | ajaxNote.failure();
149 | },
150 | onStartRequest: function(){
151 | ajaxNote.loading();
152 | },
153 | onFormInjected: function(){
154 | scrollContent();
155 | ajaxNote.success();
156 | },
157 | onSave: function(eventNames, linkElem, json){
158 | updateElementsAfterQuickEditSuccess(eventNames, linkElem, json);
159 | ajaxNote.success();
160 | },
161 | onBeforeSubmit: function(){
162 | ajaxNote.loading();
163 | jsForm.wysiwyg.each(function(editor){
164 | editor.saveContent();
165 | });
166 | }
167 | };
168 |
169 | var platforms = mainScope.getElements('.platform');
170 | var mainForm = mainScope.getElementById('main_form');
171 |
172 | mainScope.getElements('.quick_edit_select').addEvent('change', function(){
173 | var parent = this.getParent('.quick_edit_block');
174 | var optionElem = this.options[this.selectedIndex];
175 | var editElem = parent.getElement('.js_edit_field');
176 | if (editElem) {
177 | editElem.href = optionElem.get('data-edit');
178 | }
179 | var deleteElem = parent.getElement('.js_delete');
180 | if (deleteElem) {
181 | deleteElem.href = optionElem.get('data-delete');
182 | }
183 | this.getElements('.quick_edit_active').removeClass('quick_edit_active');
184 | optionElem.addClass('quick_edit_active');
185 | }).addEvent('quickEditEntryAdded', function(){
186 | this.selectedIndex = this.options.length - 1;
187 | this.fireEvent('change');
188 | }).addEvent('quickEditEntryRemoved', function(){
189 | this.selectedIndex = 0;
190 | this.fireEvent('change');
191 | }).fireEvent('change');
192 |
193 | mainScope.addEvent('click:relay(.js_delete)', function(e){
194 | e.target.addEvents({
195 | 'ajax:success': function(){
196 | updateElementsAfterQuickEditSuccess(['successAndRemove'], e.target);
197 | },
198 | 'ajax:failure': function(){
199 | alert(Locale.get('Notifications.failure'));
200 | }
201 | });
202 | });
203 |
204 | mainScope.addEvent('click:relay(.js_toggle)', function(e){
205 | e.target.addEvents({
206 | 'ajax:success': function(xhr, responseJSON){
207 | updateElementsAfterQuickEditSuccess(['successAndChange'], e.target, responseJSON);
208 | },
209 | 'ajax:failure': function(){
210 | alert(Locale.get('Notifications.failure'));
211 | }
212 | });
213 | });
214 |
215 | if (platforms.length) {
216 | quickEditOptions = Object.merge({
217 | onSuccessAndNext: function(){
218 | var tr = this.linkElem.getParent('tr');
219 | var nextTr = tr.getNext('tr');
220 |
221 | if (nextTr) {
222 | editStack.removeAllStacks();
223 | editStack.addEditBrick(quickEditOptions, nextTr.getElement('a'), nextTr);
224 | }
225 | else {
226 | var trIndex;
227 | var platform = tr.getParent('.platform');
228 | var loadMore = platform.getElement('.load_more');
229 | if (loadMore) {
230 | trIndex = tr.getParent('tbody').getElements('tr').indexOf(tr);
231 | triggerPlatformPagination(loadMore.get('href'), platform, function(){
232 | platform.getElements('tbody tr').each(function(newTr, index){
233 | if (trIndex === index) {
234 | nextTr = newTr.getNext('tr');
235 |
236 | editStack.removeAllStacks();
237 | editStack.addEditBrick(quickEditOptions, nextTr.getElement('a'), nextTr);
238 | }
239 | });
240 | });
241 | }
242 | else {
243 | nextTr = platform.getElements('tbody tr')[0];
244 |
245 | editStack.removeAllStacks();
246 | editStack.addEditBrick(quickEditOptions, nextTr.getElement('a'), nextTr);
247 | }
248 | }
249 | }
250 | }, sharedQuickEditOptions);
251 |
252 |
253 | platforms.each(function(p){
254 | new PlatformHelper(p, {
255 | onPaginationStart: function(link){
256 | triggerPlatformPagination(link.get('href'), link.getParent('.platform'));
257 | },
258 | onQuickEditStart: function(link){
259 | editStack.removeAllStacks();
260 | editStack.addEditBrick(quickEditOptions, link, link.getParent('tr'));
261 | },
262 | onSearch: function(){
263 | ajaxNote.loading();
264 | },
265 | onSearchFailure: function(){
266 | ajaxNote.failure();
267 | },
268 | onSearchSuccess: function(request){
269 | window.fireEvent('platformUpdate', [p.getParent()]);
270 | window.history.pushState({ turbolinks: true, url: request.url }, '', request.url);
271 | }
272 | });
273 | });
274 | }
275 | else if (mainForm) {
276 | quickEditOptions = Object.merge({
277 | onSuccessAndNext: function(){
278 | var a = this.linkElem;
279 | var li = a.getParent('li');
280 | if ( ! li) {
281 | this.close();
282 | return;
283 | }
284 | var holder = li.getNext('li');
285 | if ( ! holder) {
286 | holder = li.getParent('ul');
287 | }
288 |
289 | editStack.removeAllStacks();
290 | editStack.addEditBrick(quickEditOptions, holder.getElement('a'));
291 | }
292 | }, sharedQuickEditOptions);
293 |
294 | mainForm.addEvent('click:relay(.quick_edit)', function(e){
295 | e.preventDefault();
296 | editStack.removeAllStacks();
297 | editStack.addEditBrick(quickEditOptions, this);
298 | });
299 | }
300 | else if (mainScope.hasClass('quick_edit_holder')) {
301 | quickEditOptions = Object.merge({
302 | onClosed: function(){
303 | editStack.removeStack();
304 | },
305 | hideNext: true
306 | }, sharedQuickEditOptions);
307 |
308 | mainScope.addEvent('click:relay(.quick_edit)', function(e){
309 | e.preventDefault();
310 | editStack.addStack();
311 | editStack.addEditBrick(quickEditOptions, this);
312 | });
313 | }
314 |
315 | mainScope.getElements('.js_sortable').each(function(sortableElems){
316 | new Sortables(sortableElems, {
317 | handle: '.handle',
318 | onStart: function(element){
319 | element.addClass('dragged');
320 | },
321 | onComplete: function(element){
322 | element.removeClass('dragged');
323 | new Request({
324 | method: 'put',
325 | headers: {
326 | 'X-CSRF-Token': rails && rails.csrf && rails.csrf.token
327 | },
328 | url: sortableElems.get('data-sort-url')
329 | }).send({data: {order: this.serialize()}});
330 | }
331 | });
332 | });
333 |
334 | var fm = document.id('flash_massages');
335 | if (fm) {
336 | fm.removeClass.delay(10000, fm, 'show');
337 | }
338 |
339 | mainScope.getElements('.initial_referral').each(function(elem){
340 | setTimeout(function(){
341 | elem.removeClass('live_edit');
342 | setTimeout(function(){
343 | elem.removeClass('animate');
344 | }, 600);
345 | }, 500);
346 | });
347 |
348 | mainScope.getElements('.ninja_file_field').addEvent('change', function(){
349 | var text = (this.value || '').split('\\');
350 | var parent = this.getParent();
351 | var newText = parent.get('data-empty-field-text');
352 | if (this.value) {
353 | newText = text.getLast() || this.value;
354 | }
355 | parent.getElement('.h_text').set('text', newText);
356 | });
357 |
358 | mainScope.getElements('.js_submit_form_on_change').addEvent('change', function(e){
359 | e.target.getParent('form').submit();
360 | });
361 |
362 | // picture preview handler on input
363 | mainScope.getElements('.preview_input').addEvent('change', function(e) {
364 | var img = this.getNext('img');
365 | if (img) {
366 | img.src = URL.createObjectURL(e.target.files[0]);
367 | }
368 | });
369 |
370 | // picture preview handler on hover
371 | mainScope.getElements('.thumbnail_image').addEvents({
372 | mousemove: function(e) {
373 | this.getNext('img').setStyles({
374 | top: e.page.y - 260,
375 | left: e.page.x + 10
376 | });
377 | },
378 | mouseenter: function() {
379 | var popup = new Element('img', {'class': 'popped_image', src: this.src});
380 | popup.inject(this, 'after');
381 | },
382 | mouseleave: function() {
383 | mainScope.getElements('.popped_image').destroy();
384 | }
385 | });
386 |
387 | ajaxNote.success();
388 |
389 |
390 | // direct_uploads.js
391 | mainScope.addEventListener("direct-upload:initialize", function (event) {
392 | var target = event.target;
393 | var detail = event.detail;
394 | var id = detail.id;
395 | var file = detail.file;
396 | target.insertAdjacentHTML("beforebegin",
397 | '' +
398 | '
' +
399 | '
' + file.name + '' +
400 | '
'
401 | );
402 | });
403 |
404 | mainScope.addEventListener("direct-upload:start", function (event) {
405 | var id = event.detail.id;
406 | var element = document.getElementById('direct-upload-' + id);
407 | element.classList.remove("direct-upload--pending");
408 | });
409 |
410 | mainScope.addEventListener("direct-upload:progress", function (event) {
411 | var id = event.detail.id;
412 | var progress = event.detail.progress;
413 | var progressElement = document.getElementById('direct-upload-progress-' + id);
414 | progressElement.style.width = progress + "%";
415 | });
416 |
417 | mainScope.addEventListener("direct-upload:error", function (event) {
418 | event.preventDefault();
419 | var id = event.detail.id;
420 | var error = event.detail.error;
421 | var element = document.getElementById('direct-upload-' + id);
422 | element.classList.add("direct-upload--error");
423 | element.setAttribute("title", error);
424 | });
425 |
426 | mainScope.addEventListener("direct-upload:end", function (event) {
427 | var id = event.detail.id;
428 | var element = document.getElementById('direct-upload-' + id);
429 | element.classList.add("direct-upload--complete");
430 | });
431 | });
432 |
433 |
434 | var bodyCallback = function () {
435 | window.fireEvent('bhfDomChunkReady', [document.body]);
436 | };
437 | var scopeCallback = function (scope) {
438 | window.fireEvent('bhfDomChunkReady', [scope]);
439 | };
440 | document.addEventListener('turbolinks:request-start', function () {
441 | ajaxNote.loading();
442 | });
443 | document.addEventListener('turbolinks:load', bodyCallback);
444 | window.addEvent('platformUpdate', scopeCallback);
445 | window.addEvent('quickEditFormInject', scopeCallback);
446 | }());
447 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/Ajaxify.js:
--------------------------------------------------------------------------------
1 | var Ajaxify = new Class({
2 | version: 0.2,
3 |
4 | Implements: [Options, Events],
5 |
6 | options: {
7 | holder: new Element('div#ajax_holder'),
8 | fadeOutDuration: 2000
9 | },
10 |
11 | initialize: function(_options) {
12 | this.options.events = {
13 | loading: {
14 | name: 'ajax:loading',
15 | text: 'Loading…'
16 | },
17 | success: {
18 | name: 'ajax:success',
19 | text: 'Changes successfully saved!'
20 | },
21 | failure: {
22 | name: 'ajax:failure',
23 | text: Locale.get('Notifications.failure')
24 | }
25 | };
26 | this.setOptions(_options);
27 | },
28 |
29 | setup: function(){
30 | this.holder = this.options.holder.inject(document.body);
31 | },
32 |
33 | applyEvents: function(el){
34 | el = document.id(el || document.body);
35 | var apply = function(action, callback) {
36 | el.getElements('[data-remote="true"]').addEvent(action, callback);
37 | };
38 |
39 | apply(this.options.events.loading.name, this.loading.bind(this));
40 | apply(this.options.events.success.name, this.success.bind(this));
41 | apply(this.options.events.failure.name, this.failure.bind(this));
42 | },
43 |
44 | loading: function(xhr){
45 | this.setMessage('loading', false);
46 | },
47 | success: function(xhr){
48 | this.setMessage('success', true);
49 | },
50 | failure: function(xhr){
51 | this.setMessage('failure', false);
52 | },
53 |
54 | setMessage: function(status, fadeOut) {
55 | this.holder
56 | .set('text', this.options.events[status].text)
57 | .set('class', status);
58 |
59 | if (fadeOut) {
60 | this.holder.addClass('fadeout');
61 | setTimeout(function(){
62 | this.holder.erase('class');
63 | }.bind(this), this.options.fadeOutDuration);
64 | }
65 | }
66 | });
67 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/ArrayFields.js:
--------------------------------------------------------------------------------
1 | var ArrayFields = new Class({
2 | version: 0.1,
3 |
4 | // Implements: [Options, Events],
5 |
6 | initialize: function(_object, _options) {
7 | if ( ! _object) { return; }
8 | // this.setOptions(_options);
9 | var elem = _object;
10 | var template = elem.getElement('.array_fields').clone();
11 | var currentNr = 0;
12 |
13 | new Element('span.plus_button.qe_button.default_form_align', {text: '+'})
14 | .inject(elem)
15 | .addEvent('click', function(e){
16 | var holder = template.clone();
17 | var newInput = (holder.getElement('input') || holder).erase('value');
18 | var arrayI = newInput.get('name').match(/.+?\[(\d+)\].+/);
19 | if (arrayI && arrayI[1]) {
20 | currentNr += 1;
21 | newInput.set('name',
22 | newInput.get('name')
23 | .replace(/(.+?\[)\d+(\].+)/, '$1'+(parseInt(arrayI[1], 10)+currentNr)+'$2')
24 | );
25 | }
26 | holder.inject(e.target, 'before');
27 | window.fireEvent('bhfDomChunkReady', [holder]);
28 | });
29 |
30 | elem.getParent('form').addEvent('submit', function(){
31 | elem.getElements('.array_fields').each(function(fieldElem){
32 | var input = (fieldElem.getElement('input') || fieldElem);
33 | if (input.value) { return; }
34 | input.erase('name');
35 | });
36 | return true;
37 | });
38 | }
39 | });
40 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/FormHelper.js:
--------------------------------------------------------------------------------
1 | var FormHelper = new Class({
2 | version: 0.1,
3 |
4 | wysiwyg: [],
5 |
6 | options: {
7 | },
8 |
9 | // Implements: [Options, Events],
10 |
11 | setup: function(_object, _options) {
12 | if ( ! _object) { return; }
13 | // this.setOptions(_options);
14 | var scope = _object;
15 |
16 | scope.getElements('.wysiwyg').each(function(elem){
17 | this.wysiwyg.push(elem.mooEditable());
18 | }.bind(this));
19 |
20 | scope.getElements('.multiple_field').each(function(elem){
21 | new MultipleFields(elem);
22 | });
23 | scope.getElements('.array_holder').each(function(elem){
24 | new ArrayFields(elem);
25 | });
26 |
27 | scope.getElements('.picker').each(function(input){
28 | var options = {
29 | timePicker: true,
30 | format: '%B %d, %Y %H:%M'
31 | };
32 | if (input.hasClass('date')) {
33 | options = {
34 | timePicker: false,
35 | format: '%B %d, %Y'
36 | };
37 | }
38 | else if (input.hasClass('time')) {
39 | options = {
40 | pickOnly: 'time',
41 | format: '%H:%M'
42 | };
43 | }
44 |
45 | var hiddenInput = input.clone();
46 | if (input.value) {
47 | input.value = new Date().parse(input.value).format(options.format);
48 | }
49 | input.erase('name');
50 | hiddenInput.set('type', 'hidden').inject(input, 'after');
51 |
52 | new Picker.Date(input, Object.merge({
53 | onSelect: function(date){
54 | hiddenInput.value = date.format('db');
55 | }
56 | }, options));
57 | });
58 |
59 | scope.getElements('.wmd_editor').each(function(mdTa){
60 | var headline, toggleHtmlPreview, toggleLivePreview, livePreview, htmlPreview;
61 |
62 | var togglePreview = function(e){
63 | var htmlMode = e.target.hasClass('toggle_html_preview');
64 | livePreview.toggleClass('hide', htmlMode);
65 | htmlPreview.toggleClass('hide', !htmlMode);
66 | toggleLivePreview.toggleClass('active', !htmlMode);
67 | toggleHtmlPreview.toggleClass('active', htmlMode);
68 | };
69 |
70 | headline = new Element('h6.preview_switch', {text: 'Preview'});
71 |
72 | toggleHtmlPreview = new Element('span.toggle_html_preview', {text: 'HTML'})
73 | .addEvent('click', togglePreview)
74 | .inject(headline);
75 | toggleLivePreview = new Element('span.toggle_live_preview', {text: 'Live'})
76 | .addEvent('click', togglePreview)
77 | .inject(headline);
78 |
79 | headline.inject(mdTa, 'after');
80 |
81 | livePreview = new Element('div.wmd-preview.hide').inject(headline, 'after');
82 | htmlPreview = new Element('div.wmd-output.hide').inject(livePreview, 'after');
83 |
84 | new WMDEditor({
85 | input: mdTa,
86 | button_bar: new Element('div').inject(mdTa, 'before'),
87 | preview: livePreview,
88 | output: htmlPreview,
89 | buttons: 'bold italic link image ol ul heading hr undo redo',
90 | modifierKeys: false,
91 | autoFormatting: false
92 | });
93 | });
94 |
95 | scope.getElements('.map_data_lat').each(function(lat){
96 | new Setlatlng(lat);
97 | });
98 | }
99 | });
100 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/MultipleFields.js:
--------------------------------------------------------------------------------
1 | var MultipleFields = new Class({
2 | version: 0.1,
3 |
4 | options: {
5 | spliter: ';'
6 | },
7 |
8 | // Implements: [Options, Events],
9 |
10 | initialize: function(_object, _options) {
11 | if ( ! _object) { return; }
12 | // this.setOptions(_options);
13 | if (_object.get('data-spliter')) {
14 | this.options.spliter = _object.get('data-spliter');
15 | }
16 |
17 | var elem = _object;
18 | var template = elem.clone()
19 | .erase('name').erase('id').erase('data-spliter').erase('value')
20 | .addClass('template');
21 |
22 | elem.set('type', 'hidden');
23 |
24 | new Element('span.plus_button.qe_button.default_form_align', {text: '+'})
25 | .inject(elem, 'after')
26 | .addEvent('click', function(e){
27 | this.addField(elem, template);
28 | }.bind(this));
29 |
30 | elem.get('value').toString().split(this.options.spliter).each(function(data){
31 | this.addField(elem, template, data);
32 | }.bind(this));
33 | },
34 |
35 | addField: function(elem, template, data){
36 | template
37 | .clone()
38 | .set('value', data)
39 | .addEvent('change', function(e){
40 | var values = [];
41 | e.target.getParent('.input').getElements('.template').each(function(template){
42 | if (template.value.trim()) {
43 | values.push(template.value.trim());
44 | }
45 | });
46 | elem.set('value', values.join(this.options.spliter));
47 | }.bind(this))
48 | .inject(elem.getParent('.input').getElement('.plus_button'), 'before');
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/Picker.js:
--------------------------------------------------------------------------------
1 | /*
2 | ---
3 | name: Picker
4 | description: Creates a Picker, which can be used for anything
5 | authors: Arian Stolwijk
6 | requires: [Core/Element.Dimensions, Core/Fx.Tween, Core/Fx.Transitions]
7 | provides: Picker
8 | ...
9 | */
10 |
11 |
12 | var Picker = new Class({
13 |
14 | Implements: [Options, Events],
15 |
16 | options: {/*
17 | onShow: function(){},
18 | onOpen: function(){},
19 | onHide: function(){},
20 | onClose: function(){},*/
21 |
22 | pickerClass: 'datepicker',
23 | inject: null,
24 | animationDuration: 400,
25 | useFadeInOut: true,
26 | positionOffset: {x: 0, y: 0},
27 | pickerPosition: 'bottom',
28 | draggable: true,
29 | showOnInit: true,
30 | columns: 1,
31 | footer: false
32 | },
33 |
34 | initialize: function(options){
35 | this.setOptions(options);
36 | this.constructPicker();
37 | if (this.options.showOnInit) this.show();
38 | },
39 |
40 | constructPicker: function(){
41 | var options = this.options;
42 |
43 | var picker = this.picker = new Element('div', {
44 | 'class': options.pickerClass,
45 | styles: {
46 | left: 0,
47 | top: 0,
48 | display: 'none',
49 | opacity: 0
50 | }
51 | }).inject(options.inject || document.body);
52 | picker.addClass('column_' + options.columns);
53 |
54 | if (options.useFadeInOut){
55 | picker.set('tween', {
56 | duration: options.animationDuration,
57 | link: 'cancel'
58 | });
59 | }
60 |
61 | // Build the header
62 | var header = this.header = new Element('div.header').inject(picker);
63 |
64 | var title = this.title = new Element('div.title').inject(header);
65 | var titleID = this.titleID = 'pickertitle-' + String.uniqueID();
66 | this.titleText = new Element('div', {
67 | 'role': 'heading',
68 | 'class': 'titleText',
69 | 'id': titleID,
70 | 'aria-live': 'assertive',
71 | 'aria-atomic': 'true'
72 | }).inject(title);
73 |
74 | this.closeButton = new Element('div.closeButton[text=x][role=button]')
75 | .addEvent('click', this.close.pass(false, this))
76 | .inject(header);
77 |
78 | // Build the body of the picker
79 | var body = this.body = new Element('div.body').inject(picker);
80 |
81 | if (options.footer){
82 | this.footer = new Element('div.footer').inject(picker);
83 | picker.addClass('footer');
84 | }
85 |
86 | // oldContents and newContents are used to slide from the old content to a new one.
87 | var slider = this.slider = new Element('div.slider', {
88 | styles: {
89 | position: 'absolute',
90 | top: 0,
91 | left: 0
92 | }
93 | }).set('tween', {
94 | duration: options.animationDuration,
95 | transition: Fx.Transitions.Quad.easeInOut
96 | }).inject(body);
97 |
98 | this.newContents = new Element('div', {
99 | styles: {
100 | position: 'absolute',
101 | top: 0,
102 | left: 0
103 | }
104 | }).inject(slider);
105 |
106 | this.oldContents = new Element('div', {
107 | styles: {
108 | position: 'absolute',
109 | top: 0
110 | }
111 | }).inject(slider);
112 |
113 | this.originalColumns = options.columns;
114 | this.setColumns(options.columns);
115 |
116 | // IFrameShim for select fields in IE
117 | var shim = this.shim = window['IframeShim'] ? new IframeShim(picker) : null;
118 |
119 | // Dragging
120 | if (options.draggable && typeOf(picker.makeDraggable) == 'function'){
121 | this.dragger = picker.makeDraggable(shim ? {
122 | onDrag: shim.position.bind(shim)
123 | } : null);
124 | picker.setStyle('cursor', 'move');
125 | }
126 | },
127 |
128 | open: function(noFx){
129 | if (this.opened == true) return this;
130 | this.opened = true;
131 | var self = this,
132 | picker = this.picker.setStyle('display', 'block').set('aria-hidden', 'false')
133 | if (this.shim) this.shim.show();
134 | this.fireEvent('open');
135 | if (this.options.useFadeInOut && !noFx){
136 | picker.get('tween').start('opacity', 1).chain(function(){
137 | self.fireEvent('show');
138 | this.callChain();
139 | });
140 | } else {
141 | picker.setStyle('opacity', 1);
142 | this.fireEvent('show');
143 | }
144 | return this;
145 | },
146 |
147 | show: function(){
148 | return this.open(true);
149 | },
150 |
151 | close: function(noFx){
152 | if (this.opened == false) return this;
153 | this.opened = false;
154 | this.fireEvent('close');
155 | var self = this, picker = this.picker, hide = function(){
156 | picker.setStyle('display', 'none').set('aria-hidden', 'true');
157 | if (self.shim) self.shim.hide();
158 | self.fireEvent('hide');
159 | };
160 | if (this.options.useFadeInOut && !noFx){
161 | picker.get('tween').start('opacity', 0).chain(hide);
162 | } else {
163 | picker.setStyle('opacity', 0);
164 | hide();
165 | }
166 | return this;
167 | },
168 |
169 | hide: function(){
170 | return this.close(true);
171 | },
172 |
173 | toggle: function(){
174 | return this[this.opened == true ? 'close' : 'open']();
175 | },
176 |
177 | destroy: function(){
178 | this.picker.destroy();
179 | if (this.shim) this.shim.destroy();
180 | },
181 |
182 | position: function(x, y){
183 | var offset = this.options.positionOffset,
184 | scroll = document.getScroll(),
185 | size = document.getSize(),
186 | pickersize = this.picker.getSize();
187 |
188 | if (typeOf(x) == 'element'){
189 | var element = x,
190 | where = y || this.options.pickerPosition;
191 |
192 | var elementCoords = element.getCoordinates();
193 |
194 | x = (where == 'left') ? elementCoords.left - pickersize.x
195 | : (where == 'bottom' || where == 'top') ? elementCoords.left
196 | : elementCoords.right
197 | y = (where == 'bottom') ? elementCoords.bottom
198 | : (where == 'top') ? elementCoords.top - pickersize.y
199 | : elementCoords.top;
200 | }
201 |
202 | x += offset.x * ((where && where == 'left') ? -1 : 1);
203 | y += offset.y * ((where && where == 'top') ? -1: 1);
204 |
205 | if ((x + pickersize.x) > (size.x + scroll.x)) x = (size.x + scroll.x) - pickersize.x;
206 | if ((y + pickersize.y) > (size.y + scroll.y)) y = (size.y + scroll.y) - pickersize.y;
207 | if (x < 0) x = 0;
208 | if (y < 0) y = 0;
209 |
210 | this.picker.setStyles({
211 | left: x,
212 | top: y
213 | });
214 | if (this.shim) this.shim.position();
215 | return this;
216 | },
217 |
218 | setBodySize: function(){
219 | var bodysize = this.bodysize = this.body.getSize();
220 |
221 | this.slider.setStyles({
222 | width: 2 * bodysize.x,
223 | height: bodysize.y
224 | });
225 | this.oldContents.setStyles({
226 | left: bodysize.x,
227 | width: bodysize.x,
228 | height: bodysize.y
229 | });
230 | this.newContents.setStyles({
231 | width: bodysize.x,
232 | height: bodysize.y
233 | });
234 | },
235 |
236 | setColumnContent: function(column, content){
237 | var columnElement = this.columns[column];
238 | if (!columnElement) return this;
239 |
240 | var type = typeOf(content);
241 | if (['string', 'number'].contains(type)) columnElement.set('text', content);
242 | else columnElement.empty().adopt(content);
243 |
244 | return this;
245 | },
246 |
247 | setColumnsContent: function(content, fx){
248 | var old = this.columns;
249 | this.columns = this.newColumns;
250 | this.newColumns = old;
251 |
252 | content.forEach(function(_content, i){
253 | this.setColumnContent(i, _content);
254 | }, this);
255 | return this.setContent(null, fx);
256 | },
257 |
258 | setColumns: function(columns){
259 | var _columns = this.columns = new Elements, _newColumns = this.newColumns = new Elements;
260 | for (var i = columns; i--;){
261 | _columns.push(new Element('div.column').addClass('column_' + (columns - i)));
262 | _newColumns.push(new Element('div.column').addClass('column_' + (columns - i)));
263 | }
264 |
265 | var oldClass = 'column_' + this.options.columns, newClass = 'column_' + columns;
266 | this.picker.removeClass(oldClass).addClass(newClass);
267 |
268 | this.options.columns = columns;
269 | return this;
270 | },
271 |
272 | setContent: function(content, fx){
273 | if (content) return this.setColumnsContent([content], fx);
274 |
275 | // swap contents so we can fill the newContents again and animate
276 | var old = this.oldContents;
277 | this.oldContents = this.newContents;
278 | this.newContents = old;
279 | this.newContents.empty();
280 |
281 | this.newContents.adopt(this.columns);
282 |
283 | this.setBodySize();
284 |
285 | if (fx){
286 | this.fx(fx);
287 | } else {
288 | this.slider.setStyle('left', 0);
289 | this.oldContents.setStyles({left: 0, opacity: 0});
290 | this.newContents.setStyles({left: 0, opacity: 1});
291 | }
292 | return this;
293 | },
294 |
295 | fx: function(fx){
296 | var oldContents = this.oldContents,
297 | newContents = this.newContents,
298 | slider = this.slider,
299 | bodysize = this.bodysize;
300 | if (fx == 'right'){
301 | oldContents.setStyles({left: 0, opacity: 1});
302 | newContents.setStyles({left: bodysize.x, opacity: 1});
303 | slider.setStyle('left', 0).tween('left', 0, -bodysize.x);
304 | } else if (fx == 'left'){
305 | oldContents.setStyles({left: bodysize.x, opacity: 1});
306 | newContents.setStyles({left: 0, opacity: 1});
307 | slider.setStyle('left', -bodysize.x).tween('left', -bodysize.x, 0);
308 | } else if (fx == 'fade'){
309 | slider.setStyle('left', 0);
310 | oldContents.setStyle('left', 0).set('tween', {
311 | duration: this.options.animationDuration / 2
312 | }).tween('opacity', 1, 0).get('tween').chain(function(){
313 | oldContents.setStyle('left', bodysize.x);
314 | });
315 | newContents.setStyles({opacity: 0, left: 0}).set('tween', {
316 | duration: this.options.animationDuration
317 | }).tween('opacity', 0, 1);
318 | }
319 | },
320 |
321 | toElement: function(){
322 | return this.picker;
323 | },
324 |
325 | setTitle: function(content, fn){
326 | if (!fn) fn = Function.from;
327 | this.titleText.empty().adopt(
328 | Array.convert(content).map(function(item, i){
329 | return typeOf(item) == 'element'
330 | ? item
331 | : new Element('div.column', {text: fn(item, this.options)}).addClass('column_' + (i + 1));
332 | }, this)
333 | );
334 | return this;
335 | },
336 |
337 | setTitleEvent: function(fn){
338 | this.titleText.removeEvents('click');
339 | if (fn) this.titleText.addEvent('click', fn);
340 | this.titleText.setStyle('cursor', fn ? 'pointer' : '');
341 | return this;
342 | }
343 |
344 | });
345 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/Picker_Attach.js:
--------------------------------------------------------------------------------
1 | /*
2 | ---
3 | name: Picker.Attach
4 | description: Adds attach and detach methods to the Picker, to attach it to element events
5 | authors: Arian Stolwijk
6 | requires: [Picker, Core/Element.Event]
7 | provides: Picker.Attach
8 | ...
9 | */
10 |
11 |
12 | Picker.Attach = new Class({
13 |
14 | Extends: Picker,
15 |
16 | options: {/*
17 | onAttached: function(event){},
18 |
19 | toggleElements: null, // deprecated
20 | toggle: null, // When set it deactivate toggling by clicking on the input */
21 | togglesOnly: true, // set to false to always make calendar popup on input element, if true, it depends on the toggles elements set.
22 | showOnInit: false, // overrides the Picker option
23 | blockKeydown: true
24 | },
25 |
26 | initialize: function(attachTo, options){
27 | this.parent(options);
28 |
29 | this.attachedEvents = [];
30 | this.attachedElements = [];
31 | this.toggles = [];
32 | this.inputs = [];
33 |
34 | var documentEvent = function(event){
35 | if (this.attachedElements.contains(event.target)) return;
36 | this.close();
37 | }.bind(this);
38 | var document = this.picker.getDocument().addEvent('click', documentEvent);
39 |
40 | var preventPickerClick = function(event){
41 | event.stopPropagation();
42 | return false;
43 | };
44 | this.picker.addEvent('click', preventPickerClick);
45 |
46 | // Support for deprecated toggleElements
47 | if (this.options.toggleElements) this.options.toggle = document.getElements(this.options.toggleElements);
48 |
49 | this.attach(attachTo, this.options.toggle);
50 | },
51 |
52 | attach: function(attachTo, toggle){
53 | if (typeOf(attachTo) == 'string') attachTo = document.id(attachTo);
54 | if (typeOf(toggle) == 'string') toggle = document.id(toggle);
55 |
56 | var elements = Array.convert(attachTo),
57 | toggles = Array.convert(toggle),
58 | allElements = [].append(elements).combine(toggles),
59 | self = this;
60 |
61 | var closeEvent = function(event){
62 | var stopInput = self.options.blockKeydown
63 | && event.type == 'keydown'
64 | && !(['tab', 'esc'].contains(event.key)),
65 | isCloseKey = event.type == 'keydown'
66 | && (['tab', 'esc'].contains(event.key)),
67 | isA = event.target.get('tag') == 'a';
68 |
69 | if (stopInput || isA) event.preventDefault();
70 | if (isCloseKey || isA) self.close();
71 | };
72 |
73 | var getOpenEvent = function(element){
74 | return function(event){
75 | var tag = event.target.get('tag');
76 | if (tag == 'input' && event.type == 'click' && !element.match(':focus') || (self.opened && self.input == element)) return;
77 | if (tag == 'a') event.stop();
78 | self.position(element);
79 | self.open();
80 | self.fireEvent('attached', [event, element]);
81 | };
82 | };
83 |
84 | var getToggleEvent = function(open, close){
85 | return function(event){
86 | if (self.opened) close(event);
87 | else open(event);
88 | };
89 | };
90 |
91 | allElements.each(function(element){
92 |
93 | // The events are already attached!
94 | if (self.attachedElements.contains(element)) return;
95 |
96 | var events = {},
97 | tag = element.get('tag'),
98 | openEvent = getOpenEvent(element),
99 | // closeEvent does not have a depency on element
100 | toggleEvent = getToggleEvent(openEvent, closeEvent);
101 |
102 | if (tag == 'input'){
103 | // Fix in order to use togglers only
104 | if (!self.options.togglesOnly || !toggles.length){
105 | events = {
106 | focus: openEvent,
107 | click: openEvent,
108 | keydown: closeEvent
109 | };
110 | }
111 | self.inputs.push(element);
112 | } else {
113 | if (toggles.contains(element)){
114 | self.toggles.push(element);
115 | events.click = toggleEvent
116 | } else {
117 | events.click = openEvent;
118 | }
119 | }
120 | element.addEvents(events);
121 | self.attachedElements.push(element);
122 | self.attachedEvents.push(events);
123 | });
124 | return this;
125 | },
126 |
127 | detach: function(attachTo, toggle){
128 | if (typeOf(attachTo) == 'string') attachTo = document.id(attachTo);
129 | if (typeOf(toggle) == 'string') toggle = document.id(toggle);
130 |
131 | var elements = Array.convert(attachTo),
132 | toggles = Array.convert(toggle),
133 | allElements = [].append(elements).combine(toggles),
134 | self = this;
135 |
136 | if (!allElements.length) allElements = self.attachedElements;
137 |
138 | allElements.each(function(element){
139 | var i = self.attachedElements.indexOf(element);
140 | if (i < 0) return;
141 |
142 | var events = self.attachedEvents[i];
143 | element.removeEvents(events);
144 | delete self.attachedEvents[i];
145 | delete self.attachedElements[i];
146 |
147 | var toggleIndex = self.toggles.indexOf(element);
148 | if (toggleIndex != -1) delete self.toggles[toggleIndex];
149 |
150 | var inputIndex = self.inputs.indexOf(element);
151 | if (toggleIndex != -1) delete self.inputs[inputIndex];
152 | });
153 | return this;
154 | },
155 |
156 | destroy: function(){
157 | this.detach();
158 | return this.parent();
159 | }
160 |
161 | });
162 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/PlatformHelper.js:
--------------------------------------------------------------------------------
1 | var PlatformHelper = new Class({
2 | version: 0.1,
3 |
4 | options: {
5 | },
6 |
7 | Implements: [Options, Events],
8 |
9 | initialize: function(_object, _options) {
10 | if ( ! _object) { return; }
11 | this.setOptions(_options);
12 | var scope = _object;
13 | var _this = this;
14 |
15 | scope.getElements('.pagination a, thead a').addEvent('click', function(e){
16 | e.preventDefault();
17 | _this.fireEvent('paginationStart', [this])
18 | });
19 | scope.getElements('.search').addEvent('submit', function(e){
20 | _this.fireEvent('search');
21 | e.preventDefault();
22 | var hidden_search = this.getElement('.hidden_search');
23 | if (hidden_search) {
24 | hidden_search.destroy();
25 | }
26 | var request = new Request.bhf({
27 | method: 'get',
28 | url: e.target.get('action'),
29 | onFailure: function(){
30 | _this.fireEvent('searchFailure');
31 | },
32 | onSuccess: function(a, b, html){
33 | scope.innerHTML = html;
34 | _this.fireEvent('searchSuccess', [request])
35 | }
36 | }).send({data: e.target});
37 | });
38 | scope.addEvent('click:relay(.quick_edit)', function(e){
39 | e.preventDefault();
40 | _this.fireEvent('quickEditStart', [this]);
41 | });
42 | scope.addEvent('click:relay(.action a, .js_toggle)', function(e){
43 | this.addClass('clicked');
44 | setTimeout(function(){
45 | this.removeClass('clicked');
46 | }.bind(this), 1500);
47 | });
48 | scope.getElements('.js_delete').addEvent('mouseenter', function(){
49 | this.getParent('tr').addClass('background_delete');
50 | }).addEvent('mouseleave', function(){
51 | this.getParent('tr').removeClass('background_delete');
52 | });
53 | }
54 | });
55 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/QuickEdit.js:
--------------------------------------------------------------------------------
1 | var QuickEdit = new Class({
2 | version: 0.2,
3 |
4 | options: {
5 | holderParent: 'content',
6 | hideNext: false
7 | },
8 |
9 | Implements: [Options, Events],
10 |
11 | initialize: function(_options){
12 | this.setOptions(_options);
13 | this.holder = new Element('div.quick_edit_holder');
14 | },
15 |
16 | startEdit: function(element){
17 | this.linkElem = element;
18 | this.newEntry = this.linkElem.hasClass('js_add_field');
19 |
20 | this.fireEvent('startRequest');
21 | this.currentRequest = new Request.HTML({
22 | method: 'get',
23 | evalScripts: false,
24 | url: element.get('href'),
25 | onFailure: function(invalidForm){
26 | this.fireEvent('failure');
27 | }.bind(this),
28 | onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript){
29 | this.injectForm(responseHTML);
30 |
31 | var nextElem = this.holder.getElement('.save_and_next');
32 | if (this.options.hideNext && nextElem) {
33 | nextElem.addClass('hide');
34 | }
35 |
36 | window.fireEvent('quickEditFormInject', [this.holder]);
37 | }.bind(this)
38 | }).send();
39 | },
40 |
41 | submit: function(eventNames){
42 | var form = this.holder.getElement('form');
43 | this.fireEvent('beforeSubmit');
44 |
45 | new Request.JSON({
46 | method: form.get('method'),
47 | url: form.get('action'),
48 | evalScripts: true,
49 | onRequest: function(){
50 | this.disableButtons();
51 | }.bind(this),
52 | onFailure: function(invalidForm){
53 | this.injectForm(invalidForm.response);
54 | window.fireEvent('quickEditFormInject', [this.holder]);
55 | }.bind(this),
56 | onSuccess: function(json){
57 | if ( ! eventNames.contains('successAndNext')) {
58 | this.close();
59 | }
60 | eventNames.each(function(eventName){
61 | this.fireEvent(eventName, [json]);
62 | }.bind(this));
63 | this.fireEvent('save', [eventNames, this.linkElem, json]);
64 | }.bind(this)
65 | }).send({data: form});
66 | },
67 |
68 | disableButtons: function(){
69 | this.holder.getElements('.open, .cancel, .save_and_next, .save').set('disabled', 'disabled');
70 | },
71 |
72 | close: function(){
73 | if (this.currentRequest) {
74 | this.currentRequest.cancel();
75 | }
76 | this.holder.dispose();
77 | this.fireEvent('closed');
78 | },
79 |
80 | injectForm: function(form){
81 | this.holder.innerHTML = form;
82 |
83 | this.holder.getElements('.open').addEvent('click', function(e){
84 | e.preventDefault();
85 | Turbolinks.visit((this.linkElem.getElement('a') || this.linkElem).get('href'));
86 | }.bind(this));
87 | this.holder.getElements('.cancel').addEvent('click', function(e){
88 | e.preventDefault();
89 | this.close();
90 | }.bind(this));
91 | this.holder.getElements('.save_and_next').addEvent('click', function(e){
92 | e.preventDefault();
93 | this.submit(this.newEntry ? ['successAndAdd'] : ['successAndChange', 'successAndNext']);
94 | }.bind(this));
95 | this.holder.getElements('.save').addEvent('click', function(e){
96 | e.preventDefault();
97 | this.submit(this.newEntry ? ['successAndAdd'] : ['successAndChange']);
98 | }.bind(this));
99 | this.holder.getElements('form').addEvent('submit', function(e){
100 | e.preventDefault();
101 | this.submit(this.newEntry ? ['successAndAdd'] : ['successAndChange']);
102 | }.bind(this));
103 |
104 | this.holder.inject(document.id(this.options.holderParent));
105 |
106 | this.fireEvent('formInjected', [this.holder]);
107 | },
108 |
109 | hide: function(){
110 | this.holder.addClass('collapsed');
111 | },
112 |
113 | show: function(){
114 | this.holder.removeClass('collapsed');
115 | }
116 | });
117 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/QuickEditStack.js:
--------------------------------------------------------------------------------
1 | var QuickEditStack = new Class({
2 | version: 0.1,
3 |
4 | options: {
5 | },
6 | bricksArray: [],
7 | bricksIndex: 0,
8 |
9 | Implements: [Options, Events],
10 |
11 | initialize: function(_options) {
12 | this.setOptions(_options);
13 | },
14 | addEditBrick: function(editOptions, link, linkParent){
15 | var qi = this.bricksIndex;
16 | var qe = this.bricksArray[qi];
17 | if ( ! qe) {
18 | qe = new QuickEdit(editOptions);
19 | this.bricksArray.push(qe);
20 | }
21 |
22 | this.bricksArray.each(function(b, i){
23 | if (i === qi) {
24 | b.show();
25 | }
26 | else if (i > qi) {
27 | b.close();
28 | }
29 | });
30 |
31 | qe.addEvents(this.options.permanentQuickEditEvents);
32 | qe.startEdit(link);
33 | },
34 | addStack: function(){
35 | this.bricksIndex = this.bricksIndex + 1;
36 | this.bricksArray.each(function(b){
37 | b.hide();
38 | });
39 | },
40 | removeStack: function(){
41 | this.bricksArray[this.bricksIndex] = undefined;
42 | this.bricksIndex = this.bricksIndex - 1;
43 | this.bricksArray = this.bricksArray.clean();
44 | (this.bricksArray.getLast() || {show: function(){}}).show();
45 | },
46 | removeAllStacks: function(){
47 | this.bricksArray.clean().each(function(b, i){
48 | b.close();
49 | });
50 | this.bricksArray = [];
51 | this.bricksIndex = 0;
52 | }
53 | });
54 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/Request_bhf.js:
--------------------------------------------------------------------------------
1 | Request.bhf = new Class({
2 | Extends: Request.HTML,
3 |
4 | send: function(options){
5 | var progressSupport = ('onprogress' in new Browser.Request);
6 |
7 | if (!this.check(options)) return this;
8 |
9 | this.options.isSuccess = this.options.isSuccess || this.isSuccess;
10 | this.running = true;
11 |
12 | var type = typeOf(options);
13 | if (type == 'string' || type == 'element') options = {data: options};
14 |
15 | var old = this.options;
16 | options = Object.append({data: old.data, url: old.url, method: old.method}, options);
17 | var data = options.data, url = String(options.url), method = options.method.toLowerCase();
18 |
19 | switch (typeOf(data)){
20 | case 'element': data = document.id(data).toQueryString(); break;
21 | case 'object': case 'hash': data = Object.toQueryString(data);
22 | }
23 |
24 | if (this.options.format){
25 | var format = 'format=' + this.options.format;
26 | data = (data) ? format + '&' + data : format;
27 | }
28 |
29 | if (this.options.emulation && !['get', 'post'].contains(method)){
30 | var _method = '_method=' + method;
31 | data = (data) ? _method + '&' + data : _method;
32 | method = 'post';
33 | }
34 |
35 | if (this.options.urlEncoded && ['post', 'put'].contains(method)){
36 | var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
37 | this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
38 | }
39 |
40 | if (!url) url = document.location.pathname;
41 |
42 | var trimPosition = url.lastIndexOf('/');
43 | if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
44 |
45 | if (this.options.noCache)
46 | url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
47 |
48 | if (data && (method == 'get' || method == 'delete')){
49 | url += (url.indexOf('?') > -1 ? '&' : '?') + data;
50 | data = null;
51 | }
52 |
53 | var xhr = this.xhr;
54 | if (progressSupport){
55 | xhr.onloadstart = this.loadstart.bind(this);
56 | xhr.onprogress = this.progress.bind(this);
57 | }
58 |
59 | xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
60 | if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
61 |
62 | xhr.onreadystatechange = this.onStateChange.bind(this);
63 |
64 | Object.each(this.headers, function(value, key){
65 | try {
66 | xhr.setRequestHeader(key, value);
67 | } catch (e){
68 | this.fireEvent('exception', [key, value]);
69 | }
70 | }, this);
71 |
72 | this.fireEvent('request');
73 | this.url = url;
74 | xhr.send(data);
75 | if (!this.options.async) this.onStateChange();
76 | else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
77 | return this;
78 | }
79 | });
80 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/classes/Setlatlng.js:
--------------------------------------------------------------------------------
1 | var Setlatlng = new Class({
2 | Implements: [Options, Events],
3 |
4 | options: {
5 |
6 | },
7 |
8 | initialize: function(elem){
9 | if (window.google) {
10 | this.setup(elem);
11 | }
12 | else {
13 | Setlatlng.GMapsCallback = function(){
14 | this.setup(elem);
15 | }.bind(this);
16 | Asset.javascript('http://maps.googleapis.com/maps/api/js?sensor=false&callback=Setlatlng.GMapsCallback');
17 | }
18 | },
19 |
20 | setup: function(elem){
21 | var latElem = elem;
22 | var lngElem = elem.getNext('.map_data_lng');
23 | var setValues = function(lat, lng){
24 | latElem.value = lat;
25 | lngElem.value = lng;
26 | };
27 | var center = new google.maps.LatLng(
28 | latElem.value ? latElem.value : latElem.get('data-default-lat'),
29 | lngElem.value ? lngElem.value : latElem.get('data-default-lng')
30 | );
31 |
32 | var map = new google.maps.Map(new Element('div.map_canvas').inject(latElem, 'before'), {
33 | zoom: 14,
34 | mapTypeId: google.maps.MapTypeId.ROADMAP,
35 | center: center,
36 | mapTypeControl: true,
37 | zoomControl: true,
38 | zoomControlOptions: {
39 | style: google.maps.ZoomControlStyle.SMALL
40 | },
41 | streetViewControl: false,
42 | panControl: false,
43 | scaleControl: false,
44 | overviewMapControl: false
45 | });
46 |
47 | var marker = new google.maps.Marker({
48 | title: 'Location',
49 | draggable: true
50 | });
51 |
52 | if (latElem.value && lngElem.value) {
53 | marker.setPosition(new google.maps.LatLng(latElem.value, lngElem.value));
54 | marker.setMap(map);
55 | }
56 | else {
57 | if (navigator) {
58 | navigator.geolocation.getCurrentPosition(function(e){
59 | map.setCenter(new google.maps.LatLng(e.coords.latitude, e.coords.longitude));
60 | });
61 | }
62 | google.maps.event.addListener(map, 'click', function(e){
63 | marker.setPosition(e.latLng);
64 | marker.setMap(map);
65 | setValues(marker.getPosition().lat(), marker.getPosition().lng());
66 | google.maps.event.clearListeners(map, 'click');
67 | });
68 | }
69 |
70 | google.maps.event.addListener(marker, 'dragend', function(){
71 | var mPos = marker.getPosition();
72 | map.panTo(mPos);
73 | setValues(mPos.lat(), mPos.lng());
74 | });
75 | }
76 | });
77 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/locales/Locale.de-DE.js:
--------------------------------------------------------------------------------
1 | Locale.define('de-DE', 'DatePicker', {
2 | select_a_time: 'Wähle eine Zeit',
3 | use_mouse_wheel: 'Mit dem Mausrad kannst du schneller die Werte ändern',
4 | time_confirm_button: 'OK',
5 | apply_range: 'Übernehmen',
6 | cancel: 'Abbrechen',
7 | week: 'W'
8 | });
9 |
10 | Locale.define('de-DE', 'Notifications', {
11 | failure: 'Etwas ist schief gelaufen!'
12 | });
13 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/locales/Locale.en-US.js:
--------------------------------------------------------------------------------
1 | Locale.define('en-US', 'DatePicker', {
2 | select_a_time: 'Select a time',
3 | use_mouse_wheel: 'Use the mouse wheel to quickly change value',
4 | time_confirm_button: 'OK',
5 | apply_range: 'Apply',
6 | cancel: 'Cancel',
7 | week: 'Wk'
8 | });
9 |
10 |
11 | Locale.define('en-US', 'Notifications', {
12 | failure: 'Something went wrong!'
13 | });
14 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/locales/Locale.pt-PT.js:
--------------------------------------------------------------------------------
1 | Locale.define('pt-PT', 'DatePicker', {
2 | select_a_time: 'Selecione um horário',
3 | use_mouse_wheel: 'Use o scroll do mouse para rapidamente alterar valores',
4 | time_confirm_button: 'OK',
5 | apply_range: 'Aplicar',
6 | cancel: 'Cancelar',
7 | week: 'Sm'
8 | });
9 |
10 |
11 | Locale.define('pt-PT', 'Notifications', {
12 | failure: 'Algo deu errado!'
13 | });
14 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bhf/mootools_ujs.js:
--------------------------------------------------------------------------------
1 | /*
2 | ---
3 | description: A MooTools driver for the Ruby on Rails 3 unobtrusive JavaScript API.
4 |
5 | license: MIT-style
6 |
7 | authors:
8 | - Kevin Valdek
9 | - Oskar Krawczyk
10 |
11 | requires:
12 | core/1.4: '*'
13 |
14 | provides:
15 | - Rails 3 MooTools driver
16 |
17 | ...
18 | */
19 |
20 | window.addEvent('domready', function(){
21 |
22 | rails.csrf = {
23 | token: rails.getCsrf('token'),
24 | param: rails.getCsrf('param')
25 | };
26 |
27 | rails.applyEvents();
28 | });
29 |
30 | (function($){
31 |
32 | window.rails = {
33 | /**
34 | * If el is passed as argument, events will only be applied to
35 | * elements within el. Otherwise applied to document body.
36 | */
37 | applyEvents: function(el){
38 | el = $(el || document);
39 | var apply = function(selector, action, callback){
40 | el.addEvent(action + ':relay(' + selector + ')', callback);
41 | };
42 |
43 | apply('form[data-remote="true"]', 'submit', rails.handleRemote);
44 | apply('a[data-remote="true"], input[data-remote="true"]', 'click', rails.handleRemote);
45 | apply('a[data-method][data-remote!=true]', 'click', function(event){
46 | event.preventDefault();
47 | if (rails.confirmed(this)){
48 | var form = Element('form', {
49 | method: 'post',
50 | action: this.get('href'),
51 | styles: {
52 | display: 'none'
53 | }
54 | }).inject(this, 'after');
55 |
56 | var methodInput = Element('input', {
57 | type: 'hidden',
58 | name: '_method',
59 | value: this.get('data-method')
60 | });
61 |
62 | var csrfInput = Element('input', {
63 | type: 'hidden',
64 | name: rails.csrf.param,
65 | value: rails.csrf.token
66 | });
67 |
68 | form.adopt(methodInput, csrfInput).submit();
69 | }
70 | });
71 | var noMethodNorRemoteConfirm = ':not([data-method]):not([data-remote=true])[data-confirm]';
72 | apply('a' + noMethodNorRemoteConfirm + ',' + 'input' + noMethodNorRemoteConfirm, 'click', function(){
73 | return rails.confirmed(this);
74 | });
75 | },
76 |
77 | getCsrf: function(name){
78 | var meta = document.getElement('meta[name=csrf-' + name + ']');
79 | return (meta ? meta.get('content') : null);
80 | },
81 |
82 | confirmed: function(el){
83 | var confirmMessage = el.get('data-confirm');
84 | if(confirmMessage && !confirm(confirmMessage)){
85 | return false;
86 | }
87 | return true;
88 | },
89 |
90 | disable: function(el){
91 | var button = el.get('data-disable-with') ? el : el.getElement('[data-disable-with]');
92 | if (button){
93 | var enableWith = button.get('value');
94 | el.addEvent('ajax:complete', function(){
95 | button.set({
96 | value: enableWith,
97 | disabled: false
98 | });
99 | });
100 | button.set({
101 | value: button.get('data-disable-with'),
102 | disabled: true
103 | });
104 | }
105 | },
106 |
107 | handleRemote: function(event){
108 | event.preventDefault();
109 | if(rails.confirmed(this)){
110 | if (this.get('data-type') === 'json') {
111 | this.request = new Request.RailsJSON(this);
112 | }
113 | else {
114 | this.request = new Request.Rails(this);
115 | }
116 | rails.disable(this);
117 | this.request.send();
118 | }
119 | }
120 | };
121 |
122 | var _reqestClassMethods = {
123 | initialize: function(element, options){
124 | this.el = element;
125 | this.parent(Object.merge({
126 | method: this.el.get('method') || this.el.get('data-method') || 'get',
127 | url: this.el.get('action') || this.el.get('href')
128 | }, options));
129 |
130 | if (rails.csrf.token) {
131 | Object.append(this.headers, {
132 | 'X-CSRF-Token': rails.csrf.token
133 | });
134 | }
135 |
136 | this.addRailsEvents();
137 | },
138 |
139 | send: function(options) {
140 | this.el.fireEvent('ajax:before');
141 | if (this.el.get('tag') === 'form'){
142 | this.options.data = this.el;
143 | }
144 | this.parent(options);
145 | this.el.fireEvent('ajax:after', this.xhr);
146 | },
147 |
148 | addRailsEvents: function(){
149 | this.addEvent('request', function(){
150 | this.el.fireEvent('ajax:loading', this.xhr);
151 | });
152 |
153 | this.addEvent('success', function(responseJSON){
154 | this.el.fireEvent('ajax:success', [this.xhr, responseJSON]);
155 | });
156 |
157 | this.addEvent('complete', function(){
158 | this.el.fireEvent('ajax:complete', this.xhr);
159 | this.el.fireEvent('ajax:loaded', this.xhr);
160 | });
161 |
162 | this.addEvent('failure', function(){
163 | this.el.fireEvent('ajax:failure', this.xhr);
164 | });
165 | }
166 | };
167 |
168 | Request.RailsJSON = new Class(Object.merge({
169 | Extends: Request.JSON
170 | }, _reqestClassMethods));
171 |
172 | Request.Rails = new Class(Object.merge({
173 | Extends: Request
174 | }, _reqestClassMethods));
175 |
176 | })(document.id);
177 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bhf/MooEditable.scss:
--------------------------------------------------------------------------------
1 | .mooeditable-container{
2 | position: relative;
3 | border: 2px solid #ddd;
4 | }
5 |
6 | .mooeditable-ui-toolbar{
7 | color: inherit;
8 | background-color: #eee;
9 | padding: 2px;
10 | }
11 |
12 | .mooeditable-ui-toolbar:after{
13 | content: ".";
14 | display: block;
15 | height: 0;
16 | clear: both;
17 | visibility: hidden;
18 | }
19 |
20 | .mooeditable-ui-toolbar .toolbar-item,
21 | .mooeditable-ui-toolbar .toolbar-separator{
22 | display: -moz-inline-box;
23 | display: inline-block;
24 | vertical-align: middle;
25 | }
26 |
27 | .mooeditable-ui-toolbar .mooeditable-ui-button{
28 | cursor: pointer;
29 | border: 0;
30 | width: auto;
31 | height: auto;
32 | margin: 2px;
33 | padding: 2px;
34 | vertical-align: middle;
35 | color: inherit;
36 | background-color: transparent;
37 | }
38 | * html .mooeditable-ui-toolbar .mooeditable-ui-button{ /* IE6 */
39 | width: 24px;
40 | overflow: visible;
41 | padding: 2px 4px;
42 | }
43 |
44 | .mooeditable-ui-toolbar .mooeditable-ui-button.active,
45 | .mooeditable-ui-toolbar .mooeditable-ui-button.onActive{
46 | color: inherit;
47 | background-color: #ddd;
48 | }
49 |
50 | .mooeditable-ui-toolbar .mooeditable-ui-button:hover,
51 | .mooeditable-ui-toolbar .mooeditable-ui-button.hover{
52 | color: inherit;
53 | background-color: #fff;
54 | }
55 |
56 | .mooeditable-ui-toolbar .mooeditable-ui-button.disabled{
57 | cursor: default;
58 | }
59 |
60 | .mooeditable-ui-toolbar .mooeditable-ui-button.disabled:hover{
61 | color: inherit;
62 | background-color: transparent;
63 | }
64 |
65 | .mooeditable-ui-toolbar .mooeditable-ui-button .button-icon{
66 | display: -moz-inline-box;
67 | display: inline-block;
68 | width: 16px;
69 | height: 16px;
70 | vertical-align: middle;
71 | color: inherit;
72 | background: transparent url(mooeditable-toolbarbuttons-tango.png) no-repeat 0 -8px;
73 | }
74 |
75 | .mooeditable-ui-toolbar .mooeditable-ui-button .button-text{
76 | display: none;
77 | font-size: 13px;
78 | vertical-align: middle;
79 | }
80 |
81 | .mooeditable-ui-toolbar .mooeditable-ui-button-text .button-icon{
82 | display: none;
83 | }
84 | .mooeditable-ui-toolbar .mooeditable-ui-button-text .button-text{
85 | display: inline;
86 | }
87 |
88 | .mooeditable-ui-toolbar .mooeditable-ui-button-icon-text{
89 | width: auto;
90 | }
91 | .mooeditable-ui-toolbar .mooeditable-ui-button-icon-text .button-text{
92 | display: inline;
93 | margin-left: 4px;
94 | }
95 |
96 | .mooeditable-ui-toolbar .toolbar-separator{
97 | width: 1px;
98 | height: 24px;
99 | margin: 2px;
100 | text-indent: -999em;
101 | color: inherit;
102 | background-color: #fafafa;
103 | }
104 |
105 | .mooeditable-ui-toolbar .bold-item .button-icon{ background-position: 0 0; }
106 | .mooeditable-ui-toolbar .createlink-item .button-icon{ background-position: 0 -16px; }
107 | .mooeditable-ui-toolbar .indent-item .button-icon{ background-position: 0 -32px; }
108 | .mooeditable-ui-toolbar .insertorderedlist-item .button-icon{ background-position: 0 -48px; }
109 | .mooeditable-ui-toolbar .insertunorderedlist-item .button-icon{ background-position: 0 -64px; }
110 | .mooeditable-ui-toolbar .italic-item .button-icon{ background-position: 0 -80px; }
111 | .mooeditable-ui-toolbar .outdent-item .button-icon{ background-position: 0 -96px; }
112 | .mooeditable-ui-toolbar .redo-item .button-icon{ background-position: 0 -112px; }
113 | .mooeditable-ui-toolbar .strikethrough-item .button-icon{ background-position: 0 -128px; }
114 | .mooeditable-ui-toolbar .toggleview-item .button-icon{ background-position: 0 -144px; }
115 | .mooeditable-ui-toolbar .underline-item .button-icon{ background-position: 0 -160px; }
116 | .mooeditable-ui-toolbar .undo-item .button-icon{ background-position: 0 -176px; }
117 | .mooeditable-ui-toolbar .unlink-item .button-icon{ background-position: 0 -192px; }
118 | .mooeditable-ui-toolbar .urlimage-item .button-icon{ background-position: 0 -208px; }
119 |
120 | .mooeditable-iframe{
121 | margin: 0;
122 | padding: 0;
123 | border: 0;
124 | width: 100%;
125 | vertical-align: top;
126 | }
127 |
128 | .mooeditable-textarea{
129 | margin: 0 !important;
130 | padding: 0 !important;
131 | border: 0 !important;
132 | width: 100% !important;
133 | resize: none !important; /* disable resizable textareas in Webkit */
134 | outline: 0 !important; /* disable focus ring in Safari */
135 | }
136 |
137 | .mooeditable-ui-dialog{
138 | color: inherit;
139 | background-color: #ddd;
140 | position: absolute;
141 | display: block;
142 | cursor: default;
143 | font-size: 12px;
144 | z-index: 100;
145 | width: 100%;
146 | }
147 |
148 | .mooeditable-ui-dialog .dialog-content{
149 | padding: 4px 10px;
150 | }
151 |
152 | .mooeditable-ui-dialog .dialog-content *{
153 | vertical-align: middle;
154 | }
155 |
156 | .mooeditable-ui-dialog input{
157 | margin: 0 8px;
158 | }
159 |
160 | .mooeditable-ui-dialog input.text{
161 | width: 300px;
162 | }
163 |
164 | .mooeditable-ui-button-overlay{
165 | color: inherit;
166 | background-color: #ddd;
167 | font-size: 12px;
168 | z-index: 100;
169 | outline: 0;
170 | -moz-outline: 0;
171 | -webkit-outline: 0;
172 | }
173 |
174 | .mooeditable-ui-button-overlay .overlay-content{
175 | padding: 10px;
176 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/bhf/functions.sass:
--------------------------------------------------------------------------------
1 | =box-model($model: border-box)
2 | box-sizing: $model
3 | -ms-box-sizing: $model
4 | -moz-box-sizing: $model
5 | -webkit-box-sizing: $model
6 |
7 | =transition-duration($sec: 0.5)
8 | transition-duration: $sec + s
9 | -moz-transition-duration: $sec + s
10 | -webkit-transition-duration: $sec + s
11 | =transition-property($prop)
12 | transition-property: $prop
13 | -moz-transition-property: $prop
14 | -webkit-transition-property: $prop
15 | =transition-delay($sec: 0.5)
16 | transition-delay: $sec + s
17 | -moz-transition-delay: $sec + s
18 | -webkit-transition-delay: $sec + s
19 |
20 | =border-radius($radius: 4)
21 | border-radius: $radius + px
22 | -moz-border-radius: $radius + px
23 |
24 | =border-bottom-radius($radius: 4)
25 | border-radius: 0px
26 | border-bottom-right-radius: $radius + px
27 | border-bottom-left-radius: $radius + px
28 | -moz-border-radius: 0px
29 | -moz-border-radius-bottomright: $radius + px
30 | -moz-border-radius-bottomleft: $radius + px
31 |
32 | =box-shadow($x: 0, $y: 0, $blur: 0, $color: #000000)
33 | box-shadow: $x + px $y + px $blur + px $color
34 | -moz-box-shadow: $x + px $y + px $blur + px $color
35 | -webkit-box-shadow: $x + px $y + px $blur + px $color
36 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bhf/reset.sass:
--------------------------------------------------------------------------------
1 | html, body, div, span, applet, object, iframe,
2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3 | a, abbr, acronym, address, big, cite, code,
4 | del, dfn, em, font, img, ins, kbd, q, s, samp,
5 | small, strike, strong, sub, sup, tt, var,
6 | b, u, i, center,
7 | dl, dt, dd, ol, ul, li,
8 | fieldset, form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td
10 | margin: 0
11 | padding: 0
12 | border: 0
13 | outline: 0
14 | font-size: 100%
15 | vertical-align: baseline
16 | background: transparent
17 | body
18 | line-height: 1
19 | ol, ul
20 | list-style: none
21 |
22 |
23 | // remember to highlight inserts somehow!
24 | ins
25 | text-decoration: none
26 | del
27 | text-decoration: line-through
28 |
29 |
30 | // tables still need 'cellspacing="0"' in the markup
31 | table
32 | border-spacing: 0
33 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bhf/typo.scss:
--------------------------------------------------------------------------------
1 | /*
2 | 960 Grid System ~ Text CSS.
3 | Learn more ~ http://960.gs/
4 |
5 | Licensed under GPL and MIT.
6 | */
7 |
8 | /* `Basic HTML
9 | ----------------------------------------------------------------------------------------------------*/
10 |
11 | textarea,
12 | input,
13 | button,
14 | body {
15 | font: 14px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
16 | }
17 |
18 | a:focus {
19 | outline: 1px dotted;
20 | }
21 |
22 | hr {
23 | border: 0 #C8C8C8 solid;
24 | border-top-width: 1px;
25 | clear: both;
26 | height: 0;
27 | margin-bottom: 20px;
28 | }
29 |
30 | /* `Headings
31 | ----------------------------------------------------------------------------------------------------*/
32 |
33 | h1 {
34 | font-size: 25px;
35 | }
36 |
37 | h2 {
38 | font-size: 23px;
39 | }
40 |
41 | h3 {
42 | font-size: 21px;
43 | }
44 |
45 | h4 {
46 | font-size: 19px;
47 | }
48 |
49 | h5 {
50 | font-size: 17px;
51 | }
52 |
53 | h6 {
54 | font-size: 15px;
55 | }
56 |
57 | /* `Spacing
58 | ----------------------------------------------------------------------------------------------------*/
59 |
60 | ol {
61 | list-style: decimal;
62 | }
63 |
--------------------------------------------------------------------------------
/app/controllers/bhf/application_controller.rb:
--------------------------------------------------------------------------------
1 | class Bhf::ApplicationController < ActionController::Base
2 | include Bhf::Extension::ApplicationController
3 | protect_from_forgery
4 | end
5 |
--------------------------------------------------------------------------------
/app/controllers/bhf/embed_entries_controller.rb:
--------------------------------------------------------------------------------
1 | class Bhf::EmbedEntriesController < Bhf::EntriesController
2 |
3 | def new
4 | @form_url = entry_embed_index_path(@platform.name, @model.get_embedded_parent(params[:entry_id]))
5 |
6 | render 'bhf/entries/new', ({layout: 'bhf/quick_edit'} if @quick_edit) || {}
7 | end
8 |
9 | def edit
10 | @form_url = entry_embed_path(@platform.name, @model.get_embedded_parent(params[:entry_id]), @object)
11 |
12 | render 'bhf/entries/edit', ({layout: 'bhf/quick_edit'} if @quick_edit) || {}
13 | end
14 |
15 | def create
16 | before_save
17 | if @object.save
18 | manage_many_to_many
19 | after_save
20 |
21 | if @quick_edit
22 | render json: object_to_bhf_hash, status: :ok
23 | else
24 | redirect_after_save(notice: set_message('update.success', @model))
25 | end
26 | else
27 | @form_url = entry_embed_index_path(@platform.name, @model.get_embedded_parent(params[:entry_id]))
28 |
29 | r_settings = {status: :unprocessable_entity}
30 | if @quick_edit
31 | r_settings[:layout] = 'bhf/quick_edit'
32 | r_settings[:formats] = [:html]
33 | end
34 | render 'bhf/entries/new', r_settings
35 | end
36 | end
37 |
38 | def update
39 | before_save
40 | if @object.update(@permited_params)
41 | manage_many_to_many
42 | after_save
43 |
44 | if @quick_edit
45 | render json: object_to_bhf_hash, status: :ok
46 | else
47 | redirect_after_save(notice: set_message('update.success', @model))
48 | end
49 | else
50 | @form_url = entry_embed_path(@platform.name, @model.get_embedded_parent(params[:entry_id]), @object)
51 |
52 | r_settings = {status: :unprocessable_entity}
53 | if @quick_edit
54 | r_settings[:layout] = 'bhf/quick_edit'
55 | r_settings[:formats] = [:html]
56 | end
57 | render 'bhf/entries/edit', r_settings
58 | end
59 | end
60 |
61 | private
62 |
63 | def load_object
64 | @object = @model.bhf_find_embed(params[:entry_id], params[:id])
65 | @object.assign_attributes(@permited_params) if @object and @permited_params
66 | after_load
67 | end
68 |
69 | def load_new_object
70 | @object = @model.bhf_new_embed(params[:entry_id], @permited_params)
71 | after_load
72 | end
73 |
74 | def redirect_after_save(flash)
75 | if params[:return_to_new]
76 | redirect_to new_entry_embed_path(@platform.name, @model.get_embedded_parent(params[:entry_id])), flash
77 | else
78 | redirect_back_or_default(edit_entry_embed_path(@platform.name, @model.get_embedded_parent(params[:entry_id]), @object), flash)
79 | end
80 | end
81 |
82 | end
83 |
--------------------------------------------------------------------------------
/app/controllers/bhf/entries_controller.rb:
--------------------------------------------------------------------------------
1 | class Bhf::EntriesController < Bhf::ApplicationController
2 | before_action :load_platform, :load_model, :set_page, :set_quick_edit
3 | before_action :load_object, except: [:create, :new, :sort]
4 | before_action :load_new_object, only: [:create, :new]
5 |
6 | def new
7 | @form_url = entries_path(@platform.name)
8 |
9 | render layout: 'bhf/quick_edit' if @quick_edit
10 | end
11 |
12 | def edit
13 | render file: 'public/404.html', layout: false and return unless @object
14 |
15 | @form_url = entry_path(@platform.name, @object)
16 |
17 | render layout: 'bhf/quick_edit' if @quick_edit
18 | end
19 |
20 | def show
21 | render file: 'public/404.html', layout: false and return unless @object
22 |
23 | respond_to do |format|
24 | format.html
25 | format.json { render json: @object }
26 | end
27 | end
28 |
29 | def create
30 | before_save
31 | if @object.save
32 | manage_relations
33 | after_save
34 |
35 | if @quick_edit
36 | render json: object_to_bhf_hash, status: :ok
37 | else
38 | redirect_after_save(notice: set_message('create.success', @model), referral_entry: {id: @object.id, platform: @platform.name})
39 | end
40 | else
41 | @form_url = entries_path(@platform.name)
42 |
43 | r_settings = {status: :unprocessable_entity}
44 | if @quick_edit
45 | r_settings[:layout] = 'bhf/quick_edit'
46 | r_settings[:formats] = [:html]
47 | end
48 | render :new, r_settings
49 | end
50 | end
51 |
52 | def update
53 | render file: 'public/404.html', layout: false and return unless @object
54 |
55 | before_save
56 | if @object.update(@permited_params)
57 | manage_relations
58 | after_save
59 |
60 | if @quick_edit
61 | render json: object_to_bhf_hash, status: :ok
62 | else
63 | redirect_after_save(notice: set_message('update.success', @model), referral_entry: {id: @object.id, platform: @platform.name})
64 | end
65 | else
66 | @form_url = entry_path(@platform.name, @object)
67 |
68 | r_settings = {status: :unprocessable_entity}
69 | if @quick_edit
70 | r_settings[:layout] = 'bhf/quick_edit'
71 | r_settings[:formats] = [:html]
72 | end
73 | render :edit, r_settings
74 | end
75 | end
76 |
77 | def duplicate
78 | new_record = @object.dup
79 | new_record.before_bhf_duplicate(@object) if new_record.respond_to?(:before_bhf_duplicate)
80 | if new_record.save
81 | new_record.after_bhf_duplicate(@object) if new_record.respond_to?(:after_bhf_duplicate)
82 | redirect_to(page_url(@platform.page_name, anchor: "#{@platform.name}_platform"), notice: set_message('duplicate.success', @model), flash: {referral_entry: {id: new_record.id, platform: @platform.name}})
83 | else
84 | redirect_to(page_url(@platform.page_name, anchor: "#{@platform.name}_platform"), notice: set_message('duplicate.error', @model))
85 | end
86 | end
87 |
88 | def sort
89 | params[:order].each do |order|
90 | @model.
91 | find(order[1].gsub("_#{@platform.name}", '')).
92 | update_attribute(@platform.sortable_property, order[0].to_i)
93 | end
94 |
95 | head :ok
96 | end
97 |
98 | def destroy
99 | object = @object.destroy
100 | if @quick_edit
101 | respond_to do |f|
102 | f.json { render status: :ok, json: object }
103 | end
104 | else
105 | redirect_back_or_default(page_url(@platform.page_name, anchor: "#{@platform.name}_platform"), notice: set_message('destory.success', @model))
106 | end
107 | end
108 |
109 | private
110 |
111 | def object_to_bhf_hash
112 | extra_data = {
113 | to_bhf_s: @object.to_bhf_s,
114 | object_id: @object.send(@object.class.bhf_primary_key).to_s
115 | }
116 | extra_data.merge!(@object.to_bhf_hash) if @object.respond_to?(:to_bhf_hash)
117 |
118 | @platform.columns.each_with_object(extra_data) do |column, hash|
119 | respond_to_column = @object.respond_to?(column.name)
120 | column_value = @object.send(column.name) if respond_to_column
121 | custom_link = send(@platform.custom_link, platform: @platform.name, id: @object) if @platform.custom_link
122 | if column.display_type == :toggle
123 | query = {}
124 | query[@platform.model_name] = {}
125 | query[@platform.model_name][column.name] = !column_value
126 | custom_link = entry_path(@platform.name, @object, query)
127 | end
128 | edit_link = @platform.hide_edit ? "##{@object.id}_#{@platform.name}" : edit_entry_path(@platform.name, @object)
129 | partial_name = if respond_to_column && ! column.type_ignore_emtpy? && column_value.blank?
130 | 'column/empty'
131 | else
132 | "#{column.macro}/#{column.display_type}"
133 | end
134 | hash[column.name] = render_to_string(
135 | partial: "bhf/table/#{column.macro}/#{column.display_type}",
136 | formats: [:html],
137 | locals: {
138 | object: @object, column_value: column_value,
139 | link: (custom_link ? custom_link : edit_link),
140 | add_quick_link: (!custom_link && @platform.table_quick_edit)
141 | }
142 | )
143 | end
144 | end
145 |
146 | def load_platform
147 | @platform = find_platform(params[:platform])
148 | end
149 |
150 | def load_model
151 | @model = @platform.model
152 | if params[@platform.model_name.to_sym]
153 | @permited_params = params.require(@platform.model_name.to_sym).permit!
154 | end
155 | end
156 |
157 | def load_object
158 | @object = @model.unscoped.find(params[:id]) rescue nil
159 | @object.assign_attributes(@permited_params) if @object and @permited_params
160 | after_load
161 | end
162 |
163 | def load_new_object
164 | @object = @model.new(@permited_params)
165 | after_load
166 | end
167 |
168 | def manage_many_to_many
169 | return unless params[:has_and_belongs_to_many]
170 | params[:has_and_belongs_to_many].each_pair do |relation, ids|
171 | next if ids.nil? || ids.empty?
172 | reflection = @model.reflections[relation]
173 | relation_array = @object.send(relation)
174 | reflection.klass.unscoped.find(ids.keys).each do |relation_obj|
175 | has_relation = relation_array.include?(relation_obj)
176 |
177 | if ids[relation_obj.send(relation_obj.class.bhf_primary_key).to_s].blank?
178 | if has_relation
179 | relation_array.delete(relation_obj)
180 | end
181 | else
182 | if ! has_relation
183 | relation_array << relation_obj
184 | end
185 | end
186 | end
187 | end
188 | end
189 |
190 | def manage_has_one
191 | return unless params[:has_one]
192 | object_id = @object.send(@model.bhf_primary_key)
193 | params[:has_one].each_pair do |relation, id|
194 | reflection = @model.reflections[relation]
195 | reflection.klass.where(reflection.foreign_key, object_id).each do |ref_object|
196 | next if ref_object.send(reflection.klass.bhf_primary_key) == id
197 | ref_object.update_attribute(reflection.foreign_key, nil)
198 | end
199 | unless id.blank?
200 | ref_object = reflection.klass.find(id)
201 | next if ref_object.send(reflection.foreign_key) == object_id
202 | ref_object.update_attribute(reflection.foreign_key, object_id)
203 | end
204 | end
205 | end
206 |
207 | def manage_has_many
208 | return unless params[:has_many]
209 | object_id = @object.send(@model.bhf_primary_key)
210 | params[:has_many].each_pair do |relation, ids|
211 | next if ids.nil? || ids.empty?
212 | reflection = @model.reflections[relation]
213 | preset_ids = @object.send(relation).collect do |object|
214 | object.send(object.class.bhf_primary_key).to_s
215 | end
216 | @object.send(relation).klass.find(ids.keys).each do |relation_obj|
217 | fk = relation_obj.class.reflections[reflection.inverse_of.name.to_s].foreign_key
218 | relation_obj_id_as_string = relation_obj.send(relation_obj.class.bhf_primary_key).to_s
219 |
220 | if ids[relation_obj_id_as_string].blank?
221 | if preset_ids.include?(relation_obj_id_as_string)
222 | relation_obj.update_attribute(fk, nil)
223 | end
224 | else
225 | if ! preset_ids.include?(relation_obj_id_as_string)
226 | relation_obj.update_attribute(fk, object_id)
227 | end
228 | end
229 | end
230 | end
231 | end
232 |
233 | def manage_relations
234 | manage_many_to_many
235 | manage_has_many
236 | manage_has_one
237 | end
238 |
239 | def after_load
240 | @object.send(@platform.hooks(:after_load)) if @platform.hooks(:after_load)
241 | end
242 |
243 | def before_save
244 | @object.send(@platform.hooks(:before_save), params) if @platform.hooks(:before_save)
245 | end
246 |
247 | def after_save
248 | @object.send(@platform.hooks(:after_save), params) if @platform.hooks(:after_save)
249 | end
250 |
251 | def set_page
252 | @page = @platform.page_name
253 | end
254 |
255 | def set_quick_edit
256 | @quick_edit = request.xhr?
257 | end
258 |
259 | def redirect_after_save(flash)
260 | if params[:return_to_edit]
261 | redirect_to edit_entry_path(@platform.name, @object), flash
262 | elsif params[:return_to_new]
263 | redirect_to new_entry_path(@platform.name), flash
264 | else
265 | redirect_back_or_default(page_url(@platform.page_name, anchor: "#{@platform.name}_platform"), flash)
266 | end
267 | end
268 | end
269 |
--------------------------------------------------------------------------------
/app/controllers/bhf/pages_controller.rb:
--------------------------------------------------------------------------------
1 | class Bhf::PagesController < Bhf::ApplicationController
2 | before_action :set_page
3 |
4 | def show
5 | @edit_params = {}
6 | unless platform_options = @settings.content_for_page(@page)
7 | raise Exception.new("Page '#{@page}' could not be found")
8 | end
9 |
10 | if request.xhr?
11 | params.each do |key, value|
12 | return render_platform(key) if value.is_a?(ActionController::Parameters) && value[:page] != nil
13 | end
14 | end
15 |
16 | @platforms = platform_options.each_with_object([]) do |opts, obj|
17 | platform = find_platform(opts.keys[0])
18 |
19 | next if platform.table_hide?
20 | paginate_platform_objects(platform)
21 | obj << platform
22 | end
23 | end
24 |
25 | private
26 |
27 | def set_page
28 | @page = params[:page]
29 | end
30 |
31 | def render_platform(platform_name)
32 | platform = find_platform(platform_name)
33 |
34 | paginate_platform_objects(platform)
35 |
36 | render layout: false, partial: 'platform', locals: {platform: platform}
37 | end
38 |
39 | def paginate_platform_objects(platform)
40 | p = (params[platform.name] || {})
41 | page = 1
42 | unless p[:page].blank?
43 | page = p[:page].to_i
44 | end
45 |
46 | per_page = platform.pagination.offset_per_page
47 | unless p[:per_page].blank?
48 | per_page = p[:per_page].to_i
49 | end
50 |
51 | page_params = { page: page, per_page: per_page }
52 |
53 | @edit_params[platform.name] = page_params
54 |
55 | platform.get_objects(p, page_params)
56 | end
57 |
58 | end
59 |
--------------------------------------------------------------------------------
/app/helpers/bhf/application_helper.rb:
--------------------------------------------------------------------------------
1 | module Bhf
2 | module ApplicationHelper
3 |
4 | def show_t(platform)
5 | t("bhf.helpers.entry.models.#{platform.model_name}.show", platform_title: platform.title_singular.titleize, default: t('bhf.helpers.entry.show')).html_safe
6 | end
7 |
8 | def new_t(platform)
9 | t("bhf.helpers.entry.models.#{platform.model_name}.new", platform_title: platform.title_singular.titleize, default: t('bhf.helpers.entry.new')).html_safe
10 | end
11 |
12 | def duplicate_t(platform)
13 | t("bhf.helpers.entry.models.#{platform.model_name}.duplicate", platform_title: platform.title_singular.titleize, default: t('bhf.helpers.entry.duplicate')).html_safe
14 | end
15 |
16 | def edit_t(platform)
17 | t("bhf.helpers.entry.models.#{platform.model_name}.edit", platform_title: platform.title_singular.titleize, default: t('bhf.helpers.entry.edit')).html_safe
18 | end
19 |
20 | def delete_t(platform)
21 | t("bhf.helpers.entry.models.#{platform.model_name}.delete", platform_title: platform.title_singular.titleize, default: t('bhf.helpers.entry.delete')).html_safe
22 | end
23 |
24 | def find_smallest_size_url_for_file(file)
25 | if file.exists?(:thumb)
26 | file.url(:thumb)
27 | elsif file.exists?(:medium)
28 | file.url(:medium)
29 | else
30 | file.url
31 | end
32 | end
33 |
34 | def type_is_image?(type)
35 | Bhf.configuration.image_types.include?(type)
36 | end
37 |
38 | def bhf_main_logo(area)
39 | Bhf.configuration.logo.call(area)
40 | end
41 |
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/app/helpers/bhf/entries_helper.rb:
--------------------------------------------------------------------------------
1 | module Bhf
2 | module EntriesHelper
3 | include Bhf::ActionView::FormOptions
4 |
5 | def node(f, field, &block)
6 | render partial: 'bhf/helper/node', locals: {f: f, field: field, input: with_output_buffer(&block)}
7 | end
8 |
9 | def definition_item(object, column, &block)
10 | render partial: 'bhf/helper/definition_item', locals: {object: object, column: column, content: with_output_buffer(&block)}
11 | end
12 |
13 | def reflection_node(f, field, &block)
14 | return if !f.object.respond_to?(field.reflection.name) || (field.form_type == :static && f.object.new_record? && f.object.send(field.reflection.name).blank?)
15 | render partial: 'bhf/helper/reflection_node', locals: {
16 | f: f, field: field, input: with_output_buffer(&block)
17 | }
18 | end
19 |
20 | def reflection_title(f, field, count = 2)
21 | title = f.object.class.human_attribute_name(field.reflection.name)
22 | if field.link
23 | title = t("bhf.platforms.#{field.link.name}.title", count: count, default: title)
24 | end
25 | title
26 | end
27 |
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/helpers/bhf/frontend_helper.rb:
--------------------------------------------------------------------------------
1 | module Bhf
2 | module FrontendHelper
3 |
4 | def bhf_edit(object, options = {}, &block)
5 | return unless session[Bhf.configuration.session_auth_name.to_s] == true
6 |
7 | options[:platform_name] ||= object.class.to_s.pluralize.downcase
8 |
9 | if object.respond_to?(:'bhf_can_edit?', true)
10 | return unless object.bhf_can_edit?(options)
11 | end
12 |
13 | area = if options[:area]
14 | options[:area]
15 | elsif object.respond_to?(:bhf_area, true)
16 | object.bhf_area(options)
17 | end
18 |
19 | render partial: 'bhf/helper/frontend_edit', locals: { area: area, platform_name: options[:platform_name], object: object, block: (with_output_buffer(&block) if block_given?)}
20 | end
21 |
22 | def bhf_main_logo(area)
23 | Bhf.configuration.logo.call(area)
24 | end
25 |
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/helpers/bhf/pages_helper.rb:
--------------------------------------------------------------------------------
1 | module Bhf
2 | module PagesHelper
3 |
4 | def get_value(key, p)
5 | return unless p.is_a?(Hash)
6 |
7 | return p[key[0]][key[1]] if key.is_a?(Array) && p[key[0]].is_a?(Hash) && p[key[0]][key[1]].is_a?(String) #omg
8 |
9 | p[key] if p[key] && p[key].is_a?(String)
10 | end
11 |
12 | def current_order_path(order_by, platform_name)
13 | params_platfrom = params[platform_name] ? params[platform_name].clone : ActionController::Parameters.new({})
14 | params_platfrom.permit!
15 |
16 | if params_platfrom['order'] == order_by && params_platfrom['direction'] != 'desc'
17 | params_platfrom['direction'] = 'desc'
18 | else
19 | params_platfrom['direction'] = 'asc'
20 | end
21 |
22 | params_platfrom['order'] = order_by
23 |
24 | url_for platform_name => params_platfrom
25 | end
26 |
27 | def order_class(order_by, platform_name)
28 | params_platfrom = params[platform_name] ? params[platform_name] : {}
29 | return unless params_platfrom['order'] == order_by
30 |
31 | params_platfrom['direction'] == 'desc' ? 'sorted desc' : 'sorted asc'
32 | end
33 |
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/views/bhf/_footer.html.haml:
--------------------------------------------------------------------------------
1 | - if @start_time
2 | %p.state= t('bhf.footer.state', time: (Time.now-@start_time).round(2)).html_safe
3 |
4 | %p.info= t('bhf.footer.info').html_safe
5 |
6 | %p.copy= t('bhf.footer.copy').html_safe
7 |
--------------------------------------------------------------------------------
/app/views/bhf/_user.html.haml:
--------------------------------------------------------------------------------
1 | - if defined?(current_account) && current_account
2 | %p.user
3 | = t('bhf.helpers.user.login.as', user: current_account.to_bhf_s).html_safe
4 | - if main_app.respond_to?(Bhf.configuration.logout_path)
5 | = link_to t('bhf.helpers.user.logout.link'), main_app.send(Bhf.configuration.logout_path), method: :delete, class: :alt_button
6 |
--------------------------------------------------------------------------------
/app/views/bhf/application/index.html.haml:
--------------------------------------------------------------------------------
1 | .content_box
2 | %ul.basic_info_list
3 | - @settings.pages.each do |page|
4 | %li= link_to t("bhf.pages.navigation.#{page}", default: page.titleize), page_path(page)
5 |
--------------------------------------------------------------------------------
/app/views/bhf/entries/_form.html.haml:
--------------------------------------------------------------------------------
1 | - html = { id: (:main_form unless @quick_edit), multipart: (!@quick_edit && @platform.has_file_upload?), class: 'js_bhf_form' }
2 | = form_for @object, url: @form_url, builder: Bhf::ActionView::FormBuilder, html: html do |f|
3 |
4 | - if f.object.errors.any?
5 | = render partial: 'bhf/entries/validation_errors', locals: {f: f}
6 |
7 | - @platform.fields.each do |field|
8 | = render partial: "bhf/form/#{field.macro}/#{field.form_type}", locals: {f: f, field: field}
9 |
10 | - unless @quick_edit
11 |
12 | - if params[:return_to]
13 | = hidden_field :return, :to, name: :return_to, value: params[:return_to]
14 |
15 | .node
16 | .label
17 | .input
18 | - unless (f.object.new_record? && @platform.hide_create) || (!f.object.new_record? && @platform.hide_edit)
19 | = f.submit t('bhf.helpers.form.buttons.save', platform_title: @platform.title_singular)
20 | - unless @platform.hide_edit
21 | = f.submit t('bhf.helpers.form.buttons.and_edit'), name: :return_to_edit, class: 'alt_button'
22 | - unless @platform.hide_create
23 | = f.submit t('bhf.helpers.form.buttons.and_add'), name: :return_to_new, class: 'alt_button'
24 |
25 | = link_to t('bhf.helpers.entry.cancel'), (params[:return_to] ? params[:return_to] : page_path(@platform.page_name)), class: 'cancel', data: ({turbolinks: false} if params[:return_to])
26 |
--------------------------------------------------------------------------------
/app/views/bhf/entries/_validation_errors.html.haml:
--------------------------------------------------------------------------------
1 | .error_explanation
2 | %h5= t 'bhf.helpers.validation.header', error: pluralize(f.object.errors.count, t('bhf.helpers.validation.error')), name: f.object.class.model_name.human
3 | %ul
4 | - f.object.errors.each do |name, message|
5 | %li= f.error_label name, message
6 |
--------------------------------------------------------------------------------
/app/views/bhf/entries/edit.html.haml:
--------------------------------------------------------------------------------
1 | %h3= edit_t @platform
2 | = render 'bhf/entries/form'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/entries/new.html.haml:
--------------------------------------------------------------------------------
1 | %h3= new_t @platform
2 | = render 'bhf/entries/form'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/entries/show.html.haml:
--------------------------------------------------------------------------------
1 | %h3= show_t @platform
2 | - if @platform.definitions.any?
3 | .dl.content_box
4 | - @platform.definitions.each do |column|
5 | - column_value = @object.send(column.name)
6 | - if ! column_value.blank?
7 | = definition_item @object, column do
8 | = render(partial: "bhf/table/#{column.macro}/#{column.show_type}",
9 | locals: {column_value: column_value,
10 | object: @object, link: false, add_quick_link: false})
11 |
12 | - if @platform.show_extra_fields and @platform.show_extra_fields.any?
13 | .content_box
14 | - @platform.show_extra_fields.each do |name|
15 | = render(partial: "bhf/table/column/#{name}", locals: {name: name, object: @object, link: false, add_quick_link: false})
16 |
--------------------------------------------------------------------------------
/app/views/bhf/form/belongs_to/_radio.html.haml:
--------------------------------------------------------------------------------
1 | - data_source ||= field.link ? field.link.get_objects : field.reflection.klass.all
2 | - fk = field.db_name
3 | = reflection_node f, field do
4 | .quick_edit_block
5 | %ul.relation.quick_edit_inject
6 | - data_source.each do |obj|
7 | %li.quick_edit_entry
8 | = f.radio_button fk, obj.id, id: "belongs_to_#{fk}_#{obj.id}"
9 | - if field.link && ! field.link.hide_edit
10 | = link_to obj.to_bhf_s, edit_entry_path(field.link.name, obj), class: 'js_edit_field qe_button quick_edit'
11 | - else
12 | = label_tag "belongs_to_#{fk}_#{obj.id}", obj.to_bhf_s
13 | - if field.link && ! field.link.hide_delete
14 | = link_to '×'.html_safe, entry_path(field.link.name, obj), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
15 |
16 | .quick_edit_template{type: 'text/template'}
17 | %li
18 | = f.radio_button fk, '{object_id}', id: "belongs_to_#{fk}_{object_id}", class: 'js_remove_disabled', disabled: true
19 | - if field.link && ! field.link.hide_edit
20 | = link_to '{to_bhf_s}', edit_entry_path(field.link.name, '{object_id}'), class: 'js_edit_field qe_button quick_edit'
21 | - else
22 | = label_tag "belongs_to_#{fk}_#{obj.id}", obj.to_bhf_s
23 | - if field.link && ! field.link.hide_delete
24 | = link_to '×'.html_safe, entry_path(field.link.name, '{object_id}'), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
25 |
26 |
27 | - if field.link && ! field.link.hide_create
28 | = link_to '+', new_entry_path(field.link.name), class: 'js_add_field plus_button qe_button quick_edit'
29 |
--------------------------------------------------------------------------------
/app/views/bhf/form/belongs_to/_select.html.haml:
--------------------------------------------------------------------------------
1 | - data_source ||= field.link ? field.link.get_objects : field.reflection.klass.all
2 | - fk = field.db_name
3 | .node
4 | .label= f.label fk, reflection_title(f, field, 1)
5 | .input.select_input.quick_edit_block
6 | - if field.link
7 | .quick_edit_template{type: 'text/template'}
8 | = options_for_select([['{to_bhf_s}', '{object_id}', {data: {edit: edit_entry_path(field.link.name, '{object_id}'), delete: entry_path(field.link.name, '{object_id}')}}]])
9 | - select_options ||= options_from_collection_for_select_with_html_attrs(data_source, :id, :to_bhf_s, f.object.send(fk), Proc.new {|obj| {data: {edit: edit_entry_path(field.link.name, obj), delete: entry_path(field.link.name, obj)}}})
10 | - else
11 | - select_options ||= options_from_collection_for_select(data_source, :id, :to_bhf_s, f.object.send(fk))
12 |
13 | = f.select fk, select_options, {include_blank: true}, {class: ('quick_edit_inject quick_edit_select' if field.link)}
14 | = render partial: 'bhf/helper/field_errors', locals: {f: f, field: fk}
15 |
16 | - if field.link && ! field.link.hide_edit
17 | = link_to edit_t(field.link), '', class: 'js_edit_field icon edit quick_edit'
18 | - if field.link && ! field.link.hide_delete
19 | = link_to '×'.html_safe, '', method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
20 | - if field.link && ! field.link.hide_create
21 | = link_to '+', new_entry_path(field.link.name), class: 'js_add_field plus_button qe_button quick_edit'
22 |
--------------------------------------------------------------------------------
/app/views/bhf/form/belongs_to/_static.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: 'bhf/form/has_one/static', locals: {ref_object: f.object.send(field.reflection.name), field: field, f: f}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_activestorage_attachment.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | - file = f.object.send(field.name)
3 | - if file.present?
4 | = render partial: 'bhf/form/column/attachment_presenter', locals: {attachment: file.attachment}
5 |
6 | - if f.object.respond_to?(:"#{field.name}_destroy")
7 | .file_delete
8 | = f.check_box "#{field.name}_destroy"
9 | = f.label "#{field.name}_destroy", t('bhf.helpers.file.delete')
10 |
11 | = f.file_field field.name, direct_upload: true, data: {:'direct-upload-url' => main_app.rails_direct_uploads_url}
12 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_activestorage_attachments.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | - f.object.send(field.name).attachments.each do |attachment|
3 | - if attachment.present?
4 | = render partial: 'bhf/form/column/attachment_presenter', locals: {attachment: attachment}
5 | - if f.object.respond_to?(:"#{field.name}_destroy")
6 | .file_delete
7 | = f.check_box "#{field.name}_destroy"
8 | = f.label "#{field.name}_destroy", t('bhf.helpers.file.delete')
9 |
10 | =# f.file_field field.name, direct_upload: true, data: {:'direct-upload-url' => main_app.rails_direct_uploads_url}
11 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_array.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | .array_holder
3 | = f.fields_for field.name do |x|
4 | - if f.object.send(field.name).blank?
5 | = x.text_field nil, class: 'array_fields', id: nil
6 | - else
7 | - f.object.send(field.name).each do |entry|
8 | = x.text_field nil, value: entry, class: 'array_fields', id: nil
9 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_attachment_presenter.html.haml:
--------------------------------------------------------------------------------
1 | - if attachment.present?
2 | - file_blob_url = main_app.route_for(:rails_service_blob, attachment.blob.signed_id, attachment.blob.filename)
3 | - if attachment.blob.image?
4 | = link_to image_tag(main_app.route_for(:rails_representation, attachment.variant(resize: "200>")), class: 'uploaded_image'), file_blob_url
5 | - else
6 | = link_to attachment.blob.filename.to_s, file_blob_url, class: 'uploaded_file'
7 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_boolean.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.check_box field.name
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_date.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_field field.name, class: "picker #{field.type}", value: f.object.send(field.name) ? l(f.object.send(field.name), format: t("bhf.helpers.input.date_format.#{field.type}")) : nil
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_hash.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.object.send(field.name).inspect
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_hidden.html.haml:
--------------------------------------------------------------------------------
1 | = f.hidden_field field.name
2 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_image_file.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.file_field field.name, placeholder: t('bhf.helpers.form.file_field.select'), class: 'preview_input'
3 | = image_tag(f.object.send(field.name), width: 400)
4 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_mappin.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.hidden_field :lat, class: 'map_data_lat', data: {default_lat: 42.6259285}
3 | = f.hidden_field :lng, class: 'map_data_lng', data: {default_lng: -70.6519888}
4 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_markdown.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_area field.name, class: ('wmd_editor' unless @quick_edit)
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_multiple_fields.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_field field.name, class: 'multiple_field', data: {spliter: (f.object.send("#{field.name}_spliter") if f.object.respond_to?(:"#{field.name}_spliter"))}
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_number.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_field field.name, class: 'number'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_password.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.password_field field.name
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_rich_text.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.rich_text_area field.name, class: 'trix_input trix-content'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_static.html.haml:
--------------------------------------------------------------------------------
1 | - if value = f.object.send(field.name)
2 | - return if @quick_edit and field.display_type == :primary_key
3 | = node f, field do
4 | - if field.display_type == :primary_key
5 | %strong= value
6 | - elsif field.display_type == :date
7 | = l value, format: :long
8 | - else
9 | = value
10 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_string.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_field field.name
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_text.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_area field.name
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_type.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | - base = f.object.class.respond_to?(:base_class) ? f.object.class.base_class : f.object.class
3 | - list = base.descendants.map do |x|
4 | - [x.to_s.sub(base.to_s,'').titleize, x.to_s]
5 | = f.select :type, list
6 |
--------------------------------------------------------------------------------
/app/views/bhf/form/column/_wysiwyg.html.haml:
--------------------------------------------------------------------------------
1 | = node f, field do
2 | = f.text_area field.name, class: 'wysiwyg'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/form/embeds_many/_static.html.haml:
--------------------------------------------------------------------------------
1 | = reflection_node f, field do
2 | - ref_objects ||= f.object.send(field.reflection.name)
3 | - is_one = field.macro == :embeds_one
4 | .quick_edit_block
5 | %p.empty{class: ('hide' unless ref_objects.blank?)}= t 'bhf.helpers.entry.empty', count: (is_one ? 1 : 2)
6 | %ul.relation.quick_edit_inject{class: field.macro}
7 | - ref_objects.each do |ref_object|
8 | %li.quick_edit_entry
9 | - if field.link && ! field.link.hide_edit
10 | = link_to ref_object.to_bhf_s, edit_entry_embed_path(field.link.name, f.object, ref_object), class: 'js_edit_field qe_button quick_edit'
11 | - else
12 | = ref_object.to_bhf_s
13 | - if field.link && ! field.link.hide_delete
14 | = link_to '×'.html_safe, entry_embed_path(field.link.name, f.object, ref_object), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
15 |
16 | .quick_edit_template{type: 'text/template'}
17 | %li.quick_edit_entry
18 | - if field.link && ! field.link.hide_edit
19 | = link_to '{to_bhf_s}', edit_entry_embed_path(field.link.name, f.object, '{object_id}'), class: 'js_edit_field qe_button quick_edit'
20 | - else
21 | = '{to_bhf_s}'
22 | - if field.link && ! field.link.hide_delete
23 | = link_to '×'.html_safe, entry_embed_path(field.link.name, f.object, '{object_id}'), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
24 |
25 | - if ! is_one && field.link && ! field.link.hide_create
26 | = link_to '+', new_entry_embed_path(field.link.name, f.object), class: 'js_add_field plus_button qe_button quick_edit default_form_align'
27 |
--------------------------------------------------------------------------------
/app/views/bhf/form/embeds_one/_static.html.haml:
--------------------------------------------------------------------------------
1 | - ref_object ||= f.object.send(field.reflection.name)
2 | - if ref_object
3 | = render partial: 'bhf/form/embeds_many/static', locals: {ref_objects: [ref_object], field: field, f: f}
4 |
--------------------------------------------------------------------------------
/app/views/bhf/form/has_and_belongs_to_many/_check_box.html.haml:
--------------------------------------------------------------------------------
1 | - data_source ||= field.link ? field.link.get_objects : field.reflection.klass.all
2 | - check_box_type ||= :many_to_many_check_box
3 | = reflection_node f, field do
4 | .quick_edit_block
5 | %ul.relation.quick_edit_inject
6 | - data_source.each do |ref_object|
7 | %li.quick_edit_entry
8 | = f.send(check_box_type, ref_object, field.reflection.name, params, field.link && ! field.link.hide_edit)
9 | - if field.link && ! field.link.hide_edit
10 | = link_to ref_object.to_bhf_s, edit_entry_path(field.link.name, ref_object), class: 'js_edit_field qe_button quick_edit'
11 | - if field.link && ! field.link.hide_delete
12 | = link_to '×'.html_safe, entry_path(field.link.name, ref_object), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
13 |
14 | .quick_edit_template{type: 'text/template'}
15 | %li.quick_edit_entry
16 | = f.send(check_box_type, OpenStruct.new(to_bhf_s: '{to_bhf_s}'), field.reflection.name, params, field.link && ! field.link.hide_edit, true, "{object_id}", {class: 'js_remove_disabled', disabled: true}).gsub('_object_id_', '{object_id}').html_safe # fix for id attr
17 | - if field.link && ! field.link.hide_edit
18 | = link_to '{to_bhf_s}', edit_entry_path(field.link.name, '{object_id}'), class: 'js_edit_field qe_button quick_edit'
19 | - if field.link && ! field.link.hide_delete
20 | = link_to '×'.html_safe, entry_path(field.link.name, '{object_id}'), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
21 |
22 | - if field.link && ! field.link.hide_create
23 | = link_to '+', new_entry_path(field.link.name), class: 'js_add_field plus_button qe_button quick_edit default_form_align'
24 |
--------------------------------------------------------------------------------
/app/views/bhf/form/has_and_belongs_to_many/_static.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: 'bhf/form/has_many/static', locals: {ref_objects: f.object.send(field.reflection.name), field: field, f: f}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/form/has_many/_check_box.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: "bhf/form/has_and_belongs_to_many/check_box", locals: {f: f, field: field, check_box_type: :has_many_check_box}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/form/has_many/_static.html.haml:
--------------------------------------------------------------------------------
1 | = reflection_node f, field do
2 | - ref_objects ||= f.object.send(field.reflection.name)
3 | - is_one = (field.macro == :has_one || field.macro == :belongs_to)
4 | .quick_edit_block
5 | %p.empty{class: ('hide' unless ref_objects.blank?)}= t 'bhf.helpers.entry.empty', count: (is_one ? 1 : 2)
6 | %ul.relation.quick_edit_inject{class: field.macro}
7 | - ref_objects.each do |ref_object|
8 | %li.quick_edit_entry
9 | - if field.link && ! field.link.hide_edit
10 | = link_to ref_object.to_bhf_s, edit_entry_path(field.link.name, ref_object), class: 'js_edit_field qe_button quick_edit'
11 | - else
12 | = ref_object.to_bhf_s
13 | - if field.link && ! field.link.hide_delete
14 | = link_to '×'.html_safe, entry_path(field.link.name, ref_object), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
15 |
16 | .quick_edit_template{type: 'text/template'}
17 | %li.quick_edit_entry
18 | - if field.link && ! field.link.hide_edit
19 | = link_to '{to_bhf_s}', edit_entry_path(field.link.name, '{object_id}'), class: 'js_edit_field qe_button quick_edit'
20 | - else
21 | = '{to_bhf_s}'
22 | - if field.link && ! field.link.hide_delete
23 | = link_to '×'.html_safe, entry_path(field.link.name, '{object_id}'), method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
24 |
25 | - if ! is_one && field.link && ! field.link.hide_create
26 | = link_to '+', new_entry_path(field.link.name), class: 'js_add_field plus_button qe_button quick_edit default_form_align'
27 |
--------------------------------------------------------------------------------
/app/views/bhf/form/has_one/_select.html.haml:
--------------------------------------------------------------------------------
1 | - data_source ||= field.link ? field.link.get_objects : field.reflection.klass.all
2 | - fk = field.reflection.name
3 | - ref_obj = f.object.send(fk)
4 | - ref_obj_id = ref_obj ? ref_obj.send(field.reflection.klass.bhf_primary_key) : nil
5 | .node
6 | .label= f.label fk, reflection_title(f, field, 1)
7 | .input.select_input.quick_edit_block
8 | - if field.link
9 | .quick_edit_template{type: 'text/template'}
10 | = options_for_select([['{to_bhf_s}', '{object_id}', {data: {edit: edit_entry_path(field.link.name, '{object_id}'), delete: entry_path(field.link.name, '{object_id}')}}]])
11 | - select_options ||= options_from_collection_for_select_with_html_attrs(data_source, :id, :to_bhf_s, ref_obj_id, Proc.new {|obj| {data: {edit: edit_entry_path(field.link.name, obj), delete: entry_path(field.link.name, obj)}}})
12 | - else
13 | - select_options ||= options_from_collection_for_select(data_source, :id, :to_bhf_s, ref_obj_id)
14 |
15 | = select :has_one, fk, select_options, {include_blank: true}, {class: ('quick_edit_inject quick_edit_select' if field.link)}
16 | = render partial: 'bhf/helper/field_errors', locals: {f: f, field: fk}
17 |
18 | - if field.link && ! field.link.hide_edit
19 | = link_to edit_t(field.link), '', class: 'js_edit_field icon edit quick_edit'
20 | - if field.link && ! field.link.hide_delete
21 | = link_to '×'.html_safe, '', method: :delete, class: 'qe_delete js_delete', remote: true, data: {confirm: t('bhf.helpers.promts.confirm', platform_title: field.link.title_singular)}
22 | - if field.link && ! field.link.hide_create
23 | = link_to '+', new_entry_path(field.link.name), class: 'js_add_field plus_button qe_button quick_edit'
24 |
--------------------------------------------------------------------------------
/app/views/bhf/form/has_one/_static.html.haml:
--------------------------------------------------------------------------------
1 | - ref_object ||= f.object.send(field.reflection.name)
2 | - if ref_object
3 | = render partial: 'bhf/form/has_many/static', locals: {ref_objects: [ref_object], field: field, f: f}
4 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_definition_item.html.haml:
--------------------------------------------------------------------------------
1 | %p
2 | %span.dt #{object.class.human_attribute_name(column.name)}:
3 | %span.dd{class: [column.macro, column.show_type], data: {column_name: column.name}}
4 | = content
5 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_field_errors.html.haml:
--------------------------------------------------------------------------------
1 | - if f.field_has_errors?(field)
2 | %ul.errors
3 | - f.field_errors(field).each do |error|
4 | %li= error
5 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_flash.html.haml:
--------------------------------------------------------------------------------
1 | - f = {notice: (flash[:notice] if flash[:notice]), error: (flash[:error] if flash[:error])}.reject {|k, v| v.blank?}
2 | - unless f.blank?
3 | #flash_massages.show
4 | %ul
5 | - f.each do |type, msg|
6 | %li{class: type}= msg.to_s.html_safe
7 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_frontend_edit.html.haml:
--------------------------------------------------------------------------------
1 | - link_title = t('bhf.helpers.entry.edit', platform_title: object.to_bhf_s)
2 |
3 | = link_to bhf.edit_entry_url(platform_name, object, {return_to: request.original_url}.merge(area ? {bhf_area: area} : {})), class: 'bhf_edit', data: {:'no-turbolink' => true}, title: link_title do
4 | = block ? block : link_title
5 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_info.html.haml:
--------------------------------------------------------------------------------
1 | - unless info.blank?
2 | %p.info= info.html_safe
3 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_node.html.haml:
--------------------------------------------------------------------------------
1 | .node{class: field.form_type}
2 | .label= f.label field.name
3 | .input
4 | = input
5 | = render partial: 'bhf/helper/field_errors', locals: {f: f, field: field.name}
6 | = render partial: 'bhf/helper/info', locals: {info: field.info}
7 |
--------------------------------------------------------------------------------
/app/views/bhf/helper/_reflection_node.html.haml:
--------------------------------------------------------------------------------
1 | .node
2 | .label
3 | %p= reflection_title(f, field)
4 | .input
5 | = input
6 | = render partial: 'bhf/helper/field_errors', locals: {f: f, field: field.reflection.name}
7 | = render partial: 'bhf/helper/info', locals: {info: field.info}
8 |
--------------------------------------------------------------------------------
/app/views/bhf/pages/_platform.html.haml:
--------------------------------------------------------------------------------
1 | - platform.pagination.template = self
2 | - referral_id = nil
3 | - if flash[:referral_entry] && referral_entry = flash[:referral_entry].clone.symbolize_keys
4 | - referral_id = (referral_entry[:platform] == platform.name && referral_entry[:id])
5 |
6 | - if platform.custom_partial
7 | = render partial: "bhf/platform/#{platform.custom_partial}", locals: {platform: platform, referral_id: referral_id}
8 | - else
9 | - cached_partials = {}
10 | - local_names = [:column_value, :object, :link, :add_quick_link]
11 | %table.data_table{id: "#{platform.name}_platform", class: (platform.custom_columns? ? :custom_columns : :default_columns)}
12 | %caption
13 | %h4= platform.title.titleize
14 | - unless platform.hide_create
15 | %p.create= link_to new_t(platform), new_entry_path(platform.name), class: :alt_button
16 | - unless platform.custom_partial
17 | = render partial: 'search', locals: {platform: platform} if platform.search?
18 |
19 | %thead
20 | %tr
21 | - if platform.sortable
22 | %th.drag
23 | - platform.columns.each do |column|
24 | %th{class: "#{column.macro} #{column.display_type} #{order_class(column.db_name, platform.name)}"}
25 | = link_to_if column.reorderble, column.title.titleize, current_order_path(column.db_name, platform.name)
26 | %th.action
27 |
28 | %tfoot
29 | %tr
30 | - colspan_1 = platform.columns_count+1
31 | - if platform.custom_footer
32 | - colspan_1 = (platform.columns_count / 2.to_f).ceil
33 | - colspan_2 = (platform.columns_count / 2.to_f).floor
34 | %td{colspan: colspan_1}
35 | = platform.pagination.create(platform) unless platform.sortable
36 | - if platform.custom_footer
37 | %td.custom_footer_td{colspan: colspan_2}
38 | = render partial: "bhf/platform/#{platform.custom_footer}", locals: { platform: platform }
39 |
40 | %tbody.quick_edit_block{class: ('js_sortable' if platform.sortable), data: {sort_url: sort_entries_path(platform.name)}}
41 | - if platform.objects.any?
42 | - platform.objects.each do |object|
43 | - edit_link = platform.hide_edit ? "##{object.id}_#{platform.name}" : edit_entry_path(platform.name, object)
44 | - custom_link = send(platform.custom_link, platform: platform.name, id: object) if platform.custom_link
45 | %tr.quick_edit_entry{id: "#{object.id}_#{platform.name}", class: ('animate live_edit initial_referral' if referral_id == object.id)}
46 | - if platform.sortable
47 | %td.drag
48 | %span.handle.icon
49 | - platform.columns.each do |column|
50 | - respond_to_column = object.respond_to?(column.name)
51 | - column_value = object.send(column.name) if respond_to_column
52 | - if column.display_type == :toggle
53 | - query = {}
54 | - query[platform.model_name] = {}
55 | - query[platform.model_name][column.name] = !column_value
56 | - custom_link = entry_path(platform.name, object, query)
57 | - css_classes = [column.macro, column.display_type]
58 | - if column.display_type == :primary_key && column_value.to_s.length > 6
59 | - css_classes << :key_long
60 | %td{class: css_classes}
61 | - partial_name = if respond_to_column && ! column.type_ignore_emtpy? && column_value.blank?
62 | - 'column/empty'
63 | - else
64 | - "#{column.macro}/#{column.display_type}"
65 | - cached_partials[partial_name] ||= lookup_context.find_template(partial_name, ['bhf/table'], true, local_names)
66 | - locals = { column_value: column_value, object: object,
67 | link: (custom_link ? custom_link : edit_link),
68 | add_quick_link: (!custom_link && platform.table_quick_edit) }
69 | = cached_partials[partial_name].render(self, locals)
70 | -# render(partial: "bhf/table/#{column.macro}/#{column.display_type}", locals: {column_value: column_value, object: object})
71 |
72 | %td.action{class: ('wider' if platform.show_duplicate && !platform.hide_edit && !platform.hide_delete)}
73 | - if platform.show_duplicate
74 | - dt = duplicate_t(platform)
75 | = link_to dt, duplicate_entry_path(platform.name, object), class: 'icon duplicate', method: :post, title: dt
76 | - unless platform.hide_edit
77 | = link_to edit_t(platform), edit_link, class: 'icon edit'
78 | - unless platform.hide_delete
79 | = link_to delete_t(platform), entry_path(platform.name, object), method: :delete, remote: true, class: 'icon delete js_delete', data: {type: :json, confirm: t('bhf.helpers.promts.confirm', platform_title: platform.title_singular.titleize)}
80 | - else
81 | %td.no_entries{colspan: platform.columns_count}
82 | = t 'bhf.pagination.info', name: platform.title.titleize, count: 0
83 | - unless platform.hide_create
84 | = link_to new_t(platform), new_entry_path(platform.name)
85 |
86 | %template.quick_edit_template{type: 'text/template'}
87 | %tr.quick_edit_entry{id: "{object_id}_#{platform.name}"}
88 | - edit_link = edit_entry_path(platform.name, '{object_id}')
89 | - if platform.sortable
90 | %td.drag
91 | %span.handle.icon
92 | - platform.columns.each do |column|
93 | - css_classes = [column.macro, column.display_type]
94 | %td{class: css_classes}
95 | = "{#{column.name}}"
96 |
97 | %td.action{class: ('wider' if platform.show_duplicate && !platform.hide_edit && !platform.hide_delete)}
98 | - if platform.show_duplicate
99 | - dt = duplicate_t(platform)
100 | = link_to dt, duplicate_entry_path(platform.name, '{object_id}'), class: 'icon duplicate', method: :post, title: dt
101 | - unless platform.hide_edit
102 | = link_to edit_t(platform), edit_link, class: 'icon edit'
103 | - unless platform.hide_delete
104 | = link_to delete_t(platform), entry_path(platform.name, '{object_id}'), method: :delete, remote: true, class: 'icon delete js_delete', data: {type: :json, confirm: t('bhf.helpers.promts.confirm', platform_title: platform.title_singular.titleize)}
105 |
--------------------------------------------------------------------------------
/app/views/bhf/pages/_search.html.haml:
--------------------------------------------------------------------------------
1 | = form_for platform.name, html: {method: :get, class: :search} do |f|
2 | - p = params[platform.name]
3 | = f.fields_for :search do |s|
4 |
5 | - if platform.scopes
6 | = f.select :scope, options_from_collection_for_select(platform.scopes, :value, :name, (p && p[:scope])), {}, class: 'js_submit_form_on_change'
7 |
8 | %p.info= platform.pagination.info platform
9 |
10 | - if platform.search_field?
11 | = s.text_field :text, value: get_value([:search, :text], p), placeholder: t('bhf.helpers.searchform.placeholder')
12 |
13 | - if p && p[:search].is_a?(Hash)
14 | - p[:search].each do |key, value|
15 | = s.hidden_field key, value: value, id: nil, class: (:hidden_search if key == 'text')
16 |
17 | - if platform.custom_search
18 | = render partial: "bhf/platform/#{platform.custom_search}", locals: { platform: platform, f: f, s: s, p: p }
19 |
20 | = f.hidden_field :per_page, value: get_value(:per_page, p)
21 | = f.hidden_field :order, value: get_value(:order, p)
22 | = f.hidden_field :direction, value: get_value(:direction, p)
23 |
--------------------------------------------------------------------------------
/app/views/bhf/pages/show.html.haml:
--------------------------------------------------------------------------------
1 | - @platforms.each do |platform|
2 | .platform= render partial: 'platform', locals: {platform: platform}
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/belongs_to/_default.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: 'bhf/table/has_one/default', locals: {column_value: column_value, object: object, link: link, add_quick_link: add_quick_link}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_array.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | - if column_value.is_a?(String)
3 | = column_value
4 | - else
5 | = column_value[0..5].join(', ')
6 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_boolean.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = t("bhf.helpers.boolean.#{!!column_value}")
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_carrierwave.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | - value = column_value.respond_to?(:midem) && column_value.midem || column_value
3 | = image_tag value, alt: column_value, class: 'thumbnail_image'
4 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_date.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | - if column_value.is_a?(String)
3 | = column_value
4 | - else
5 | = l(column_value, format: :short)
6 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_empty.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = ' '.html_safe
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_extern_link.html.haml:
--------------------------------------------------------------------------------
1 | = link_to column_value, title: column_value, target: '_blank' do
2 | %strong= t('bhf.helpers.link', link: column_value).html_safe
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_file.html.haml:
--------------------------------------------------------------------------------
1 | = link_to column_value, column_value
2 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_hash.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = column_value.inspect
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_image.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = image_tag column_value, alt: column_value, class: 'preview_image'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_number.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = column_value
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_primary_key.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | %strong= column_value
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_string.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = column_value
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_text.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = sanitize(column_value.to_s, tags: %w(strong em))
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_thumbnail.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | = image_tag column_value, alt: column_value, class: 'thumbnail_image'
3 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_toggle.html.haml:
--------------------------------------------------------------------------------
1 | - if link
2 | = link_to t('bhf.helpers.entry.toggle'), link, method: :patch, remote: true, class: ['icon toggle js_toggle', ('h_true' if column_value)], data: {type: :json}
3 | - else
4 | = t("bhf.helpers.boolean.#{!!column_value}")
5 |
--------------------------------------------------------------------------------
/app/views/bhf/table/column/_type.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | - base = object.class.respond_to?(:base_class) ? object.class.base_class : object.class
3 | = column_value.sub(base.to_s, '').titleize
4 |
--------------------------------------------------------------------------------
/app/views/bhf/table/embeds_many/_default.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: 'bhf/table/has_many/default', locals: {column_value: column_value, object: object, link: link, add_quick_link: add_quick_link}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/table/embeds_one/_default.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: 'bhf/table/has_one/default', locals: {column_value: column_value, object: object, link: link, add_quick_link: add_quick_link}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/table/has_and_belongs_to_many/_default.html.haml:
--------------------------------------------------------------------------------
1 | = render partial: 'bhf/table/has_many/default', locals: {column_value: column_value, object: object, link: link, add_quick_link: add_quick_link}
2 |
--------------------------------------------------------------------------------
/app/views/bhf/table/has_many/_default.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | - if column_value.is_a?(String)
3 | = column_value
4 | - else
5 | = column_value.collect(&:to_bhf_s).join(', ')
6 |
--------------------------------------------------------------------------------
/app/views/bhf/table/has_one/_default.html.haml:
--------------------------------------------------------------------------------
1 | = content_tag link ? :a : :span, class: ('quick_edit' if add_quick_link), href: (link if link) do
2 | - if column_value
3 | - if column_value.is_a?(String)
4 | = column_value
5 | - else
6 | = column_value.to_bhf_s
7 |
--------------------------------------------------------------------------------
/app/views/kaminari/bhf/_gap.html.haml:
--------------------------------------------------------------------------------
1 | .gap= t('bhf.pagination.truncate')
2 |
--------------------------------------------------------------------------------
/app/views/kaminari/bhf/_next_page.html.haml:
--------------------------------------------------------------------------------
1 | = link_to_unless current_page.last?, t('bhf.pagination.next_label'), url, remote: remote do |name|
2 | %span= name
3 |
--------------------------------------------------------------------------------
/app/views/kaminari/bhf/_page.html.haml:
--------------------------------------------------------------------------------
1 | = link_to_unless page.current?, page, url, remote: remote, class: 'page_number' do |name|
2 | %em= name
3 |
--------------------------------------------------------------------------------
/app/views/kaminari/bhf/_paginator.html.haml:
--------------------------------------------------------------------------------
1 | = paginator.render do
2 | = prev_page_tag
3 |
4 | - each_page do |page|
5 | - if page.left_outer? || page.right_outer? || page.inside_window?
6 | = page_tag page
7 | - elsif !page.was_truncated?
8 | = gap_tag
9 |
10 | = next_page_tag
11 |
--------------------------------------------------------------------------------
/app/views/kaminari/bhf/_prev_page.html.haml:
--------------------------------------------------------------------------------
1 | = link_to_unless current_page.first?, t('bhf.pagination.previous_label'), url, remote: remote do |name|
2 | %span= name
3 |
--------------------------------------------------------------------------------
/app/views/layouts/bhf/application.haml:
--------------------------------------------------------------------------------
1 | !!!
2 | %html{lang: I18n.locale}
3 | %head
4 | %meta{charset: 'utf-8'}
5 | %meta{name: 'turbolinks-cache-control', content: 'no-cache'}
6 | %title= @title
7 | = csrf_meta_tags
8 | %link{href: "/favicon.ico?#{@app_title.to_s.downcase}", rel: 'icon', type: 'image/x-icon'}
9 | - Bhf.configuration.css.each do |css|
10 | = stylesheet_link_tag css
11 | - Bhf.configuration.js.each do |js|
12 | = javascript_include_tag js
13 |
14 | %body
15 |
16 | = render partial: 'bhf/helper/flash', locals: {flash: flash}
17 | %header
18 | %h1
19 | - logo = bhf_main_logo(params[:bhf_area])
20 |
21 | = link_to image_tag(logo), @root_link ? @root_link : main_app.root_url, data: {turbolinks: false}
22 |
23 | - if @areas && @areas.length > 1
24 | = select_tag :area_select, options_from_collection_for_select(@areas, 'path', 'name', main_app.bhf_path(params[:bhf_area])), class: 'icon'
25 |
26 | - if @settings && @settings.pages.any?
27 | %nav
28 | %ul{class: ('shrink_space' if @settings.pages.count > 7)}
29 | - @settings.pages.each do |page|
30 | %li{class: ('active' if @page == page)}
31 | = link_to t("bhf.pages.navigation.#{page}", default: page.titleize), page_path(page)
32 |
33 | #content
34 | #main
35 | = yield
36 |
37 | %footer
38 | = render 'bhf/user'
39 | = render 'bhf/footer'
40 |
--------------------------------------------------------------------------------
/app/views/layouts/bhf/quick_edit.haml:
--------------------------------------------------------------------------------
1 | .quick_edit_helper
2 | %a.alt_button.cancel= t('bhf.quick_edit.buttons.cancel')
3 | - unless @platform.table_hide?
4 | %a.alt_button.open= t('bhf.quick_edit.buttons.open')
5 | = yield
6 | .button.group
7 | %button.save{class: ('create' if @object.new_record?)}= @object.new_record? ? t('bhf.quick_edit.buttons.create') : t('bhf.quick_edit.buttons.save')
8 | - unless @object.new_record?
9 | %button.save_and_next.icon{title: t('bhf.quick_edit.buttons.save_and_next')}= t('bhf.quick_edit.buttons.save_and_next')
10 |
--------------------------------------------------------------------------------
/bhf.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE DIRECTLY
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4 | # -*- encoding: utf-8 -*-
5 | # stub: bhf 1.0.0.beta16 ruby lib
6 |
7 | Gem::Specification.new do |s|
8 | s.name = "bhf".freeze
9 | s.version = "1.0.0.beta16"
10 |
11 | s.required_rubygems_version = Gem::Requirement.new("> 1.3.1".freeze) if s.respond_to? :required_rubygems_version=
12 | s.require_paths = ["lib".freeze]
13 | s.authors = ["Anton Pawlik".freeze]
14 | s.date = "2023-04-11"
15 | s.description = "A simple to use Rails-Engine-Gem that offers an admin interface for trusted user. Easy integratable and highly configurable and agnostic. Works with ActiveRecord and Mongoid.".freeze
16 | s.email = "anton.pawlik@gmail.com".freeze
17 | s.extra_rdoc_files = [
18 | "LICENSE.txt",
19 | "README.md"
20 | ]
21 | s.files = [
22 | ".editorconfig",
23 | "Gemfile",
24 | "Gemfile.lock",
25 | "LICENSE.txt",
26 | "README.md",
27 | "Rakefile",
28 | "VERSION",
29 | "app/assets/images/bhf/ajax_loader.gif",
30 | "app/assets/images/bhf/bg.png",
31 | "app/assets/images/bhf/logo_bhf.svg",
32 | "app/assets/images/bhf/mooeditable-toolbarbuttons-tango.png",
33 | "app/assets/images/bhf/pictos.png",
34 | "app/assets/images/bhf/pictos_2x.png",
35 | "app/assets/images/bhf/small_ajax_loader.gif",
36 | "app/assets/images/bhf/small_ajax_loader_h.gif",
37 | "app/assets/images/bhf/wmd-buttons.png",
38 | "app/assets/javascripts/bhf/MooTools-More-1.6.0.js",
39 | "app/assets/javascripts/bhf/application.js",
40 | "app/assets/javascripts/bhf/classes/Ajaxify.js",
41 | "app/assets/javascripts/bhf/classes/ArrayFields.js",
42 | "app/assets/javascripts/bhf/classes/FormHelper.js",
43 | "app/assets/javascripts/bhf/classes/MooEditable.js",
44 | "app/assets/javascripts/bhf/classes/MultipleFields.js",
45 | "app/assets/javascripts/bhf/classes/Picker.js",
46 | "app/assets/javascripts/bhf/classes/Picker_Attach.js",
47 | "app/assets/javascripts/bhf/classes/Picker_Date.js",
48 | "app/assets/javascripts/bhf/classes/PlatformHelper.js",
49 | "app/assets/javascripts/bhf/classes/QuickEdit.js",
50 | "app/assets/javascripts/bhf/classes/QuickEditStack.js",
51 | "app/assets/javascripts/bhf/classes/Request_bhf.js",
52 | "app/assets/javascripts/bhf/classes/Setlatlng.js",
53 | "app/assets/javascripts/bhf/classes/showdown.js",
54 | "app/assets/javascripts/bhf/classes/wmd.js",
55 | "app/assets/javascripts/bhf/locales/Locale.de-DE.js",
56 | "app/assets/javascripts/bhf/locales/Locale.en-US.js",
57 | "app/assets/javascripts/bhf/locales/Locale.pt-PT.js",
58 | "app/assets/javascripts/bhf/mootools_ujs.js",
59 | "app/assets/javascripts/bhf/trix.js",
60 | "app/assets/stylesheets/bhf/MooEditable.scss",
61 | "app/assets/stylesheets/bhf/application.sass",
62 | "app/assets/stylesheets/bhf/functions.sass",
63 | "app/assets/stylesheets/bhf/reset.sass",
64 | "app/assets/stylesheets/bhf/trix.scss",
65 | "app/assets/stylesheets/bhf/typo.scss",
66 | "app/controllers/bhf/application_controller.rb",
67 | "app/controllers/bhf/embed_entries_controller.rb",
68 | "app/controllers/bhf/entries_controller.rb",
69 | "app/controllers/bhf/pages_controller.rb",
70 | "app/helpers/bhf/application_helper.rb",
71 | "app/helpers/bhf/entries_helper.rb",
72 | "app/helpers/bhf/frontend_helper.rb",
73 | "app/helpers/bhf/pages_helper.rb",
74 | "app/views/bhf/_footer.html.haml",
75 | "app/views/bhf/_user.html.haml",
76 | "app/views/bhf/application/index.html.haml",
77 | "app/views/bhf/entries/_form.html.haml",
78 | "app/views/bhf/entries/_validation_errors.html.haml",
79 | "app/views/bhf/entries/edit.html.haml",
80 | "app/views/bhf/entries/new.html.haml",
81 | "app/views/bhf/entries/show.html.haml",
82 | "app/views/bhf/form/belongs_to/_radio.html.haml",
83 | "app/views/bhf/form/belongs_to/_select.html.haml",
84 | "app/views/bhf/form/belongs_to/_static.html.haml",
85 | "app/views/bhf/form/column/_activestorage_attachment.html.haml",
86 | "app/views/bhf/form/column/_activestorage_attachments.html.haml",
87 | "app/views/bhf/form/column/_array.html.haml",
88 | "app/views/bhf/form/column/_attachment_presenter.html.haml",
89 | "app/views/bhf/form/column/_boolean.html.haml",
90 | "app/views/bhf/form/column/_date.html.haml",
91 | "app/views/bhf/form/column/_hash.html.haml",
92 | "app/views/bhf/form/column/_hidden.html.haml",
93 | "app/views/bhf/form/column/_image_file.html.haml",
94 | "app/views/bhf/form/column/_mappin.html.haml",
95 | "app/views/bhf/form/column/_markdown.html.haml",
96 | "app/views/bhf/form/column/_multiple_fields.html.haml",
97 | "app/views/bhf/form/column/_number.html.haml",
98 | "app/views/bhf/form/column/_password.html.haml",
99 | "app/views/bhf/form/column/_rich_text.html.haml",
100 | "app/views/bhf/form/column/_static.html.haml",
101 | "app/views/bhf/form/column/_string.html.haml",
102 | "app/views/bhf/form/column/_text.html.haml",
103 | "app/views/bhf/form/column/_type.html.haml",
104 | "app/views/bhf/form/column/_wysiwyg.html.haml",
105 | "app/views/bhf/form/embeds_many/_static.html.haml",
106 | "app/views/bhf/form/embeds_one/_static.html.haml",
107 | "app/views/bhf/form/has_and_belongs_to_many/_check_box.html.haml",
108 | "app/views/bhf/form/has_and_belongs_to_many/_static.html.haml",
109 | "app/views/bhf/form/has_many/_check_box.html.haml",
110 | "app/views/bhf/form/has_many/_static.html.haml",
111 | "app/views/bhf/form/has_one/_select.html.haml",
112 | "app/views/bhf/form/has_one/_static.html.haml",
113 | "app/views/bhf/helper/_definition_item.html.haml",
114 | "app/views/bhf/helper/_field_errors.html.haml",
115 | "app/views/bhf/helper/_flash.html.haml",
116 | "app/views/bhf/helper/_frontend_edit.html.haml",
117 | "app/views/bhf/helper/_info.html.haml",
118 | "app/views/bhf/helper/_node.html.haml",
119 | "app/views/bhf/helper/_reflection_node.html.haml",
120 | "app/views/bhf/pages/_platform.html.haml",
121 | "app/views/bhf/pages/_search.html.haml",
122 | "app/views/bhf/pages/show.html.haml",
123 | "app/views/bhf/table/belongs_to/_default.html.haml",
124 | "app/views/bhf/table/column/_array.html.haml",
125 | "app/views/bhf/table/column/_boolean.html.haml",
126 | "app/views/bhf/table/column/_carrierwave.html.haml",
127 | "app/views/bhf/table/column/_date.html.haml",
128 | "app/views/bhf/table/column/_empty.html.haml",
129 | "app/views/bhf/table/column/_extern_link.html.haml",
130 | "app/views/bhf/table/column/_file.html.haml",
131 | "app/views/bhf/table/column/_hash.html.haml",
132 | "app/views/bhf/table/column/_image.html.haml",
133 | "app/views/bhf/table/column/_number.html.haml",
134 | "app/views/bhf/table/column/_primary_key.html.haml",
135 | "app/views/bhf/table/column/_string.html.haml",
136 | "app/views/bhf/table/column/_text.html.haml",
137 | "app/views/bhf/table/column/_thumbnail.html.haml",
138 | "app/views/bhf/table/column/_toggle.html.haml",
139 | "app/views/bhf/table/column/_type.html.haml",
140 | "app/views/bhf/table/embeds_many/_default.html.haml",
141 | "app/views/bhf/table/embeds_one/_default.html.haml",
142 | "app/views/bhf/table/has_and_belongs_to_many/_default.html.haml",
143 | "app/views/bhf/table/has_many/_default.html.haml",
144 | "app/views/bhf/table/has_one/_default.html.haml",
145 | "app/views/kaminari/bhf/_gap.html.haml",
146 | "app/views/kaminari/bhf/_next_page.html.haml",
147 | "app/views/kaminari/bhf/_page.html.haml",
148 | "app/views/kaminari/bhf/_paginator.html.haml",
149 | "app/views/kaminari/bhf/_prev_page.html.haml",
150 | "app/views/layouts/bhf/application.haml",
151 | "app/views/layouts/bhf/quick_edit.haml",
152 | "bhf.gemspec",
153 | "config/locales/de.yml",
154 | "config/locales/en.yml",
155 | "config/locales/pt.yml",
156 | "config/routes.rb",
157 | "lib/bhf.rb",
158 | "lib/bhf/action_view/form_builder.rb",
159 | "lib/bhf/action_view/form_options.rb",
160 | "lib/bhf/active_record/base.rb",
161 | "lib/bhf/controller/extension.rb",
162 | "lib/bhf/mongoid/document.rb",
163 | "lib/bhf/platform/attribute/abstract.rb",
164 | "lib/bhf/platform/attribute/column.rb",
165 | "lib/bhf/platform/attribute/reflection.rb",
166 | "lib/bhf/platform/base.rb",
167 | "lib/bhf/platform/pagination.rb",
168 | "lib/bhf/settings/base.rb",
169 | "lib/bhf/settings/platform.rb",
170 | "lib/bhf/settings/yaml_parser.rb",
171 | "lib/rails/generators/bhf/templates/initializer.rb",
172 | "test/database.yml",
173 | "test/test_helper.rb"
174 | ]
175 | s.homepage = "https://antpaw.github.io/bhf".freeze
176 | s.licenses = ["MIT".freeze]
177 | s.rubygems_version = "3.0.9".freeze
178 | s.summary = "A simple to use Rails-Engine-Gem that offers an admin interface for trusted user.".freeze
179 |
180 | if s.respond_to? :specification_version then
181 | s.specification_version = 4
182 |
183 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
184 | s.add_runtime_dependency(%q.freeze, [">= 5.2.2.1"])
185 | s.add_runtime_dependency(%q.freeze, [">= 6"])
186 | s.add_runtime_dependency(%q.freeze, [">= 2"])
187 | s.add_runtime_dependency(%q.freeze, [">= 0"])
188 | s.add_runtime_dependency(%q.freeze, [">= 0"])
189 | s.add_development_dependency(%q.freeze, [">= 3"])
190 | s.add_development_dependency(%q.freeze, [">= 3"])
191 | s.add_development_dependency(%q.freeze, [">= 1"])
192 | s.add_development_dependency(%q.freeze, [">= 2"])
193 | s.add_development_dependency(%q.freeze, [">= 0"])
194 | else
195 | s.add_dependency(%q.freeze, [">= 5.2.2.1"])
196 | s.add_dependency(%q.freeze, [">= 6"])
197 | s.add_dependency(%q.freeze, [">= 2"])
198 | s.add_dependency(%q.freeze, [">= 0"])
199 | s.add_dependency(%q.freeze, [">= 0"])
200 | s.add_dependency(%q.freeze, [">= 3"])
201 | s.add_dependency(%q.freeze, [">= 3"])
202 | s.add_dependency(%q.freeze, [">= 1"])
203 | s.add_dependency(%q.freeze, [">= 2"])
204 | s.add_dependency(%q.freeze, [">= 0"])
205 | end
206 | else
207 | s.add_dependency(%q.freeze, [">= 5.2.2.1"])
208 | s.add_dependency(%q.freeze, [">= 6"])
209 | s.add_dependency(%q.freeze, [">= 2"])
210 | s.add_dependency(%q.freeze, [">= 0"])
211 | s.add_dependency(%q.freeze, [">= 0"])
212 | s.add_dependency(%q.freeze, [">= 3"])
213 | s.add_dependency(%q.freeze, [">= 3"])
214 | s.add_dependency(%q.freeze, [">= 1"])
215 | s.add_dependency(%q.freeze, [">= 2"])
216 | s.add_dependency(%q.freeze, [">= 0"])
217 | end
218 | end
219 |
220 |
--------------------------------------------------------------------------------
/config/locales/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | bhf:
3 | page_title: "%{title} – Admin"
4 | areas:
5 | page_title: "%{area} – %{title} – Admin"
6 | helpers:
7 | boolean:
8 | 'true': 'ja'
9 | 'false': 'nein'
10 | link: 'Link ⬀'
11 | form:
12 | file_field:
13 | select: Datei wählen
14 | buttons:
15 | save: '%{platform_title} speichern'
16 | and_edit: und editieren
17 | and_add: und anlegen
18 | searchform:
19 | placeholder: Suche
20 | promts:
21 | confirm: 'Wollen Sie diesen %{platform_title}-Eintrag löschen?'
22 | entry:
23 | show: "%{platform_title} anzeigen"
24 | new: "%{platform_title} anlegen"
25 | duplicate: "%{platform_title} duplizieren"
26 | toggle: "umschalten"
27 | edit: "%{platform_title} editieren"
28 | delete: "%{platform_title} löschen"
29 | empty: Leer
30 | cancel: Abbrechen
31 | user:
32 | login:
33 | success: Login war erfolgreich
34 | error: Konnte nicht eingeloggen
35 | as: 'Sie sind als %{user} eingeloggt'
36 | logout:
37 | link: Ausloggen
38 | activated:
39 | success: "%{model} wurde erfolgreich aktiviert"
40 | error: "%{model} Fehler"
41 | validation:
42 | header: 'Durch diese Fehler konnte "%{name}" nicht gespeichert werden:'
43 | error: Fehler
44 | file:
45 | delete: Datei löschen
46 | reselect: Bitte die Datei noch mal auswählen
47 | image:
48 | delete: Bild löschen
49 |
50 | quick_edit:
51 | buttons:
52 | cancel: Abbrechen
53 | open: Öffnen
54 | save: Speichern
55 | create: Erstellen
56 | save_and_next: Speichern und weiter zum nächsten Eintrag gehen
57 |
58 | pagination:
59 | previous_label: « Vorheriges
60 | next_label: Nächstes »
61 | truncate: …
62 | load_more: Mehr anzeigen
63 | load_less: Weniger anzeigen
64 | entries: Einträge
65 | info:
66 | default: "Es werden %{offset_start} - %{offset_end} von %{count} %{name} angezeigt"
67 | other: "Es werden alle %{count} %{name} angezeigt"
68 | one: "Es wird 1 %{name} angezeigt"
69 | zero: "Es wurde nichts gefunden"
70 |
71 | footer:
72 | state: 'Seite wurde in ~%{time} Sekunden erstellt'
73 | copy: 'Gemacht von @antpaw'
74 | info: 'Mehr Informationen auf GitHub'
75 |
76 | attributes:
77 | _id: 'id'
78 |
79 | activerecord:
80 | notices:
81 | messages:
82 | duplicate:
83 | success: "%{model} wurde erfolgreich dupliziert"
84 | error: "%{model} Fehler"
85 | create:
86 | success: "%{model} wurde erfolgreich erstellt"
87 | error: "%{model} Fehler"
88 | update:
89 | success: "%{model} wurde erfolgreich editiert"
90 | error: "%{model} Fehler"
91 | destory:
92 | success: "%{model} wurde erfolgreich gelöscht"
93 | error: "%{model} Fehler"
94 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | bhf:
3 | page_title: "%{title} – Admin"
4 | areas:
5 | page_title: "%{area} – %{title} – Admin"
6 | helpers:
7 | boolean:
8 | 'true': 'yes'
9 | 'false': 'no'
10 | link: Link ⬀
11 | form:
12 | file_field:
13 | select: Choose file
14 | buttons:
15 | save: 'Save %{platform_title}'
16 | and_edit: and edit
17 | and_add: and add another
18 | searchform:
19 | placeholder: Search
20 | promts:
21 | confirm: 'Do you want to delete this %{platform_title}-entry?'
22 | entry:
23 | new: "New %{platform_title}"
24 | show: "Show %{platform_title}"
25 | duplicate: "Duplicate %{platform_title}"
26 | toggle: "Toggle"
27 | edit: "Edit %{platform_title}"
28 | delete: "Delete %{platform_title}"
29 | empty: Empty
30 | cancel: Cancel
31 | user:
32 | login:
33 | success: Login was successful
34 | error: "Can't login"
35 | as: 'You are logged in as %{user}'
36 | logout:
37 | link: Logout
38 | activated:
39 | success: "%{model} was activated successfully"
40 | error: "%{model} error"
41 | validation:
42 | header: '%{error} prohibited this "%{name}" from being saved:'
43 | error: error
44 | file:
45 | delete: Delete file
46 | reselect: Please reselect the file
47 | image:
48 | delete: Delete image
49 | input:
50 | date_format:
51 | timestamp: '%Y-%m-%d %H:%M'
52 | datetime: '%Y-%m-%d %H:%M'
53 | date: '%Y-%m-%d'
54 | time: '%H:%M'
55 | quick_edit:
56 | buttons:
57 | cancel: Cancel
58 | open: Open
59 | save: Save
60 | create: Create
61 | save_and_next: Save and go to the next entry
62 |
63 | pagination:
64 | previous_label: « Previous
65 | next_label: Next »
66 | truncate: …
67 | load_more: Show more entries
68 | load_less: Show less entries
69 | entries: entries
70 | info:
71 | default: "Displaying %{offset_start} - %{offset_end} of %{count} %{name} in total"
72 | other: "Displaying all %{count} %{name}"
73 | one: "Displaying 1 %{name}"
74 | zero: "No %{name} found"
75 |
76 | footer:
77 | state: 'Page rendered in ~%{time} seconds'
78 | copy: 'Made by @antpaw'
79 | info: 'More information on GitHub'
80 |
81 | attributes:
82 | _id: 'id'
83 |
84 | activerecord:
85 | notices:
86 | messages:
87 | duplicate:
88 | success: "%{model} was duplicated successfully"
89 | error: "%{model} error"
90 | create:
91 | success: "%{model} was created successfully"
92 | error: "%{model} error"
93 | update:
94 | success: "%{model} was updated successfully"
95 | error: "%{model} error"
96 | destory:
97 | success: "%{model} was deleted successfully"
98 | error: "%{model} error"
99 |
--------------------------------------------------------------------------------
/config/locales/pt.yml:
--------------------------------------------------------------------------------
1 | pt:
2 | bhf:
3 | page_title: "%{title} – Admin"
4 | areas:
5 | page_title: "%{area} – %{title} – Admin"
6 | helpers:
7 | boolean:
8 | 'true': 'sim'
9 | 'false': 'não'
10 | link: Link ⬀
11 | form:
12 | file_field:
13 | select: Escolher arquivo
14 | buttons:
15 | save: 'Salvar %{platform_title}'
16 | and_edit: e editar
17 | and_add: e adicionar outro
18 | searchform:
19 | placeholder: Buscar
20 | promts:
21 | confirm: 'Você quer deletar o registro: %{platform_title}?'
22 | entry:
23 | new: "Criar %{platform_title}"
24 | show: "Exibir %{platform_title}"
25 | duplicate: "Duplicar %{platform_title}"
26 | edit: "Editar %{platform_title}"
27 | delete: "Deletar %{platform_title}"
28 | empty: Esvaziar
29 | cancel: Cancelar
30 | user:
31 | login:
32 | success: Login efetuado com sucesso
33 | error: "Login não pode ser feito"
34 | as: 'Você está logado como %{user}'
35 | logout:
36 | link: Sair
37 | activated:
38 | success: "%{model} foi ativado com sucesso"
39 | error: "Erro ao ativar %{model}"
40 | validation:
41 | header: '%{error} impediu esse "%{name}" de ser salvo:'
42 | error: erro
43 | file:
44 | delete: Deletar arquivo
45 | reselect: "Por favor, selecione novamente o arquivo"
46 | image:
47 | delete: Deletar imagem
48 | input:
49 | date_format:
50 | timestamp: '%d-%m-%Y %H:%M'
51 | datetime: '%d-%m-%Y %H:%M'
52 | date: '%d-%m-%Y'
53 | time: '%H:%M'
54 | quick_edit:
55 | buttons:
56 | cancel: Cancelar
57 | open: Abrir
58 | save: Salvar
59 | create: Criar
60 | save_and_next: Salvar e ir para o próximo registro
61 |
62 | pagination:
63 | previous_label: « Anterior
64 | next_label: Próximo »
65 | truncate: …
66 | load_more: Exibir mais registros
67 | load_less: Exibir menos registros
68 | entries: registros
69 | info:
70 | default: "Exibindo %{offset_start} - %{offset_end} de %{count} %{name} no total"
71 | other: "Exibindo todos %{count} %{name}"
72 | one: "Exibindo 1 %{name}"
73 | zero: "Nenhum(a) %{name} encontrado"
74 |
75 | footer:
76 | state: 'Página renderizada em ~%{time} segundos'
77 | copy: 'Feito por @antpaw'
78 | info: 'Mais informações no GitHub'
79 |
80 | attributes:
81 | _id: 'id'
82 |
83 | activerecord:
84 | notices:
85 | messages:
86 | duplicate:
87 | success: "%{model} duplicado com sucesso"
88 | error: "Erro ao duplicar %{model}"
89 | create:
90 | success: "%{model} criado com sucesso"
91 | error: "Erro ao criar %{model}"
92 | update:
93 | success: "%{model} atualizado com sucesso"
94 | error: "Erro ao atualizar %{model}"
95 | destory:
96 | success: "%{model} deletado com sucesso"
97 | error: "Erro ao deletar %{model}"
98 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Bhf::Engine.routes.draw do
2 |
3 | root to: 'application#index'
4 |
5 | get 'page/:page', to: 'pages#show', as: :page
6 |
7 | scope ':platform' do
8 | resources :entries, except: [:index] do
9 | put :sort, on: :collection
10 | post :duplicate, on: :member
11 |
12 | resources :embed_entries, except: [:index], as: :embed
13 | end
14 | end
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/lib/bhf.rb:
--------------------------------------------------------------------------------
1 | require 'haml'
2 | require 'turbolinks'
3 | require 'kaminari'
4 |
5 | module Bhf
6 | class Engine < Rails::Engine
7 |
8 | isolate_namespace Bhf
9 |
10 | initializer 'bhf.helper' do
11 | ActiveSupport.on_load :action_controller do
12 | helper Bhf::FrontendHelper
13 | end
14 | end
15 |
16 | initializer 'bhf.model_hooks' do
17 | ActiveSupport.on_load :active_record do
18 | include Bhf::ActiveRecord::Base
19 | end
20 | ActiveSupport.on_load :mongoid do
21 | include Bhf::Mongoid::Document
22 | end
23 | end
24 |
25 | initializer 'bhf.assets.precompile' do |app|
26 | app.config.assets.precompile += %w( bhf/application.css bhf/application.js bhf/logo_bhf.svg )
27 | end
28 |
29 | end
30 |
31 |
32 | def self.configuration
33 | @configuration ||= Bhf::Configuration.new
34 | end
35 | def self.configure
36 | yield configuration
37 | end
38 | class Configuration
39 | include ActiveSupport::Configurable
40 |
41 | config_accessor(:logo) { lambda { |area| 'bhf/logo_bhf.svg' }}
42 | config_accessor(:on_login_fail) { :root_url }
43 | config_accessor(:logout_path) { :logout_path }
44 | config_accessor(:session_auth_name) { :is_admin }
45 | config_accessor(:session_account_id) { :admin_account_id }
46 | config_accessor(:account_model) { 'User' }
47 | config_accessor(:account_model_find_method) { 'find' }
48 | config_accessor(:css) { ['bhf/application'] }
49 | config_accessor(:js) { ['bhf/application'] }
50 | config_accessor(:abstract_settings) { [] }
51 | config_accessor(:image_types) {
52 | ['image/jpeg', 'image/pjpeg', 'image/jpg', 'image/png',
53 | 'image/tif', 'image/gif']
54 | }
55 | end
56 | end
57 |
58 | require 'bhf/settings/base'
59 | require 'bhf/settings/platform'
60 | require 'bhf/settings/yaml_parser'
61 | require 'bhf/platform/base'
62 | require 'bhf/platform/pagination'
63 | require 'bhf/platform/attribute/abstract'
64 | require 'bhf/platform/attribute/column'
65 | require 'bhf/platform/attribute/reflection'
66 | require 'bhf/active_record/base'
67 | require 'bhf/mongoid/document'
68 | require 'bhf/action_view/form_options'
69 | require 'bhf/action_view/form_builder'
70 | require 'bhf/controller/extension'
71 |
--------------------------------------------------------------------------------
/lib/bhf/action_view/form_builder.rb:
--------------------------------------------------------------------------------
1 | module Bhf::ActionView
2 | class FormBuilder < ActionView::Helpers::FormBuilder
3 | include ActionView::Helpers::FormTagHelper
4 |
5 | def error_label(name, message)
6 | label name, "#{object.class.human_attribute_name(name)} #{message}"
7 | end
8 |
9 | def field_errors(field)
10 | object.errors[field.to_sym]
11 | end
12 |
13 | def field_has_errors?(field)
14 | field_errors(field).any?
15 | end
16 |
17 | def many_to_many_or_has_many_check_box(mm, obj, ref_name, params, hide_label = false, checked = false, bhf_primary_key = nil, extra_html_attrs = {})
18 | bhf_primary_key ||= obj.send(obj.class.bhf_primary_key).to_s
19 | unless checked
20 | checked = if params[mm] && params[mm][ref_name]
21 | params[mm][ref_name][bhf_primary_key] != ''
22 | else
23 | object.send(ref_name).include?(obj)
24 | end
25 | end
26 |
27 | html = hidden_field_tag("#{mm}[#{ref_name}][#{bhf_primary_key}]", '', extra_html_attrs.merge(id: "hidden_#{mm}_#{ref_name}_#{bhf_primary_key}"))
28 | html = html+' '+check_box_tag("#{mm}[#{ref_name}][#{bhf_primary_key}]", bhf_primary_key, checked, extra_html_attrs)
29 | html = html+' '+label_tag("#{mm}_#{ref_name}_#{bhf_primary_key}", obj.to_bhf_s) unless hide_label
30 |
31 | html
32 | end
33 |
34 | def many_to_many_check_box(obj, ref_name, params, hide_label = false, checked = false, bhf_primary_key = nil, extra_html_attrs = {})
35 | many_to_many_or_has_many_check_box(:has_and_belongs_to_many, obj, ref_name, params, hide_label, checked, bhf_primary_key, extra_html_attrs)
36 | end
37 |
38 | def has_many_check_box(obj, ref_name, params, hide_label = false, checked = false, bhf_primary_key = nil, extra_html_attrs = {})
39 | many_to_many_or_has_many_check_box(:has_many, obj, ref_name, params, hide_label, checked, bhf_primary_key, extra_html_attrs)
40 | end
41 |
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/bhf/action_view/form_options.rb:
--------------------------------------------------------------------------------
1 | module Bhf::ActionView
2 | module FormOptions
3 |
4 | def option_groups_from_collection_for_select_with_html_attrs(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key, html_attrs)
5 | collection.map do |group|
6 | option_tags = options_from_collection_for_select_with_html_attrs(
7 | group.send(group_method), option_key_method, option_value_method, selected_key, html_attrs)
8 |
9 | content_tag(:optgroup, option_tags, label: group.send(group_label_method))
10 | end.join.html_safe
11 | end
12 |
13 | def options_from_collection_for_select_with_html_attrs(collection, value_method, text_method, selected, html_attrs)
14 | options = collection.map do |element|
15 | [
16 | value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element),
17 | html_attrs.call(element)
18 | ]
19 | end
20 | selected, disabled = extract_selected_and_disabled(selected)
21 | select_deselect = {
22 | selected: extract_values_from_collection(collection, value_method, selected),
23 | disabled: extract_values_from_collection(collection, value_method, disabled)
24 | }
25 |
26 | options_for_select(options, select_deselect)
27 | end
28 |
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/bhf/active_record/base.rb:
--------------------------------------------------------------------------------
1 | module Bhf::ActiveRecord
2 | module Base
3 |
4 | extend ActiveSupport::Concern
5 |
6 | def to_bhf_s
7 | return title if self.respond_to? :title
8 | return name if self.respond_to? :name
9 | return headline if self.respond_to? :headline
10 |
11 | if self.respond_to?(:attributes)
12 | return title if attributes['title']
13 | return name if attributes['name']
14 | klass_name = if self.class.respond_to?(:model_name)
15 | self.class.model_name.human
16 | else
17 | self.class.to_s.humanize
18 | end
19 | return "#{klass_name} ID: #{send(self.class.primary_key)}" if attributes[self.class.primary_key]
20 | end
21 |
22 | self.to_s.humanize
23 | end
24 |
25 | module ClassMethods
26 | def bhf_default_search(search_params)
27 | return where([]) if (search_term = search_params['text']).blank?
28 | where_statement = []
29 | columns_hash.each_pair do |name, props|
30 | is_number = search_term.to_i.to_s == search_term || search_term.to_f.to_s == search_term
31 |
32 | if props.type == :string || props.type == :text
33 | where_statement << "LOWER(#{name}) LIKE LOWER('%#{search_term}%')"
34 | elsif props.type == :integer && is_number
35 | where_statement << "#{name} = #{search_term.to_i}"
36 | elsif props.type == :float && is_number
37 | where_statement << "#{name} = #{search_term.to_f}"
38 | end
39 | end
40 |
41 | where(where_statement.join(' OR '))
42 | end
43 |
44 | def bhf_attribute_method?(column_name)
45 | column_names.include?(column_name)
46 | end
47 |
48 | def bhf_primary_key
49 | primary_key
50 | end
51 |
52 | def bhf_embedded?
53 | false
54 | end
55 | end
56 |
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/lib/bhf/controller/extension.rb:
--------------------------------------------------------------------------------
1 | module Bhf
2 | module Extension
3 | module ApplicationController
4 | extend ActiveSupport::Concern
5 |
6 | included do
7 | include Instance
8 |
9 | before_action :init_time
10 | before_action :check_admin_account, :setup_current_account,
11 | :load_settings, :set_title, :set_areas
12 |
13 | helper_method :current_account
14 | layout 'bhf/application'
15 | end
16 |
17 | module Instance
18 | def index
19 |
20 | end
21 |
22 | protected
23 |
24 | def check_admin_account
25 | if session[Bhf.configuration.session_auth_name.to_s] == true
26 | return true
27 | end
28 | redirect_to(main_app.send(Bhf.configuration.on_login_fail.to_sym),
29 | error: t('bhf.helpers.user.login.error')) and return false
30 | end
31 |
32 | def setup_current_account
33 | if session[Bhf.configuration.session_account_id]
34 | @current_account =
35 | Bhf.configuration.account_model.classify.constantize.send(
36 | Bhf.configuration.account_model_find_method.to_sym,
37 | session[Bhf.configuration.session_account_id.to_s]
38 | )
39 | # => User.find(current_account.id)
40 | end
41 | end
42 |
43 | def current_account
44 | @current_account
45 | end
46 |
47 | def get_account_roles(area = nil)
48 | return unless current_account
49 |
50 | if area
51 | if current_account.respond_to?(:bhf_area_roles)
52 | return current_account.bhf_area_roles(area).collect(&:identifier)
53 | end
54 | end
55 |
56 | if current_account.respond_to?(:bhf_roles)
57 | current_account.bhf_roles.collect(&:identifier)
58 | end
59 | end
60 |
61 | def load_settings
62 | yaml_parser = Bhf::Settings::YAMLParser.new(get_account_roles(
63 | params[:bhf_area]), params[:bhf_area])
64 | @settings =
65 | Bhf::Settings::Base.new(yaml_parser.settings_hash, current_account)
66 | end
67 |
68 | def set_title
69 | @app_title = Rails.application.class.to_s.split('::').first
70 |
71 | @title = if params[:bhf_area]
72 | t("bhf.areas.page_title.#{params[:bhf_area]}",
73 | area: params[:bhf_area],
74 | title: @app_title,
75 | default: t('bhf.areas.page_title',
76 | title: @app_title,
77 | area: t("bhf.areas.links.#{params[:bhf_area]}",
78 | default: params[:bhf_area])
79 | )
80 | )
81 | else
82 | t('bhf.page_title', title: @app_title)
83 | end.html_safe
84 | end
85 |
86 | def set_areas
87 | @areas = []
88 | if current_account and current_account.respond_to?(:bhf_areas)
89 | current_account.bhf_areas.each do |area|
90 | selected = params[:bhf_area] == area.identifier
91 | @areas << OpenStruct.new(
92 | name: area.to_bhf_s,
93 | selected: selected,
94 | path: main_app.bhf_path(area.identifier)
95 | )
96 | @root_link = area.link if selected
97 | end
98 | end
99 | end
100 |
101 | def set_message(type, model = nil)
102 | key = model && ActiveModel::Naming.singular(model)
103 | I18n.t("bhf.activerecord.notices.models.#{key}.#{type}",
104 | model: model.model_name.human,
105 | default: I18n.t("activerecord.notices.messages.#{type}"))
106 | end
107 |
108 | def init_time
109 | @start_time = Time.zone.now
110 | end
111 |
112 | def find_platform(platform_name)
113 | Bhf::Platform::Base.new(@settings.find_platform_settings(platform_name))
114 | end
115 |
116 | def redirect_back_or_default(default, msg)
117 | redirect_to(params[:return_to] || default, flash: msg)
118 | end
119 | end
120 | end
121 | end
122 | end
123 |
--------------------------------------------------------------------------------
/lib/bhf/mongoid/document.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Mongoid
2 | module Document
3 |
4 | extend ActiveSupport::Concern
5 |
6 | class Field
7 | attr_reader :type, :name
8 |
9 | def initialize(mongoid_field)
10 | @name = mongoid_field.name
11 | @type = mongoid_field.type.to_s.downcase.gsub('mongoid::', '').to_sym
12 | @type = :primary_key if @type == :'bson::objectid'
13 | end
14 | end
15 |
16 | class Reflection
17 | attr_reader :name, :macro, :klass, :foreign_key
18 |
19 | def initialize(mongoid_field)
20 | @name = mongoid_field.name
21 | @klass = mongoid_field.class_name.constantize
22 | @foreign_key = mongoid_field.key
23 | @macro = mongoid_field.class.name.demodulize.underscore.to_sym
24 | # @macro = case mongoid_field.macro
25 | # when :references_and_referenced_in_many
26 | # :has_and_belongs_to_many
27 | # when :references_many
28 | # :has_many
29 | # when :references_one
30 | # :has_one
31 | # when :referenced_in
32 | # :belongs_to
33 | # else
34 | # mongoid_field.macro
35 | # end
36 | end
37 | end
38 |
39 | def to_bhf_s
40 | return title if self.respond_to? :title
41 | return name if self.respond_to? :name
42 |
43 | if self.respond_to?(:attributes)
44 | return title if attributes['title']
45 | return name if attributes['name']
46 | klass_name = if self.class.respond_to?(:model_name)
47 | self.class.model_name.human
48 | else
49 | self.class.to_s.humanize
50 | end
51 | return "#{klass_name} ID: #{send(self.class.bhf_primary_key)}" if attributes[self.class.bhf_primary_key]
52 | end
53 |
54 | self.to_s.humanize
55 | end
56 |
57 | module ClassMethods
58 | def columns_hash
59 | c = {}
60 | fields.each_pair do |key, meta|
61 | next if meta.options[:metadata]
62 | next if key == '_type'
63 | c[key] = Field.new(meta)
64 | end
65 | c
66 | end
67 |
68 | def reflections
69 | c = {}
70 | relations.each do |key, meta|
71 | next if meta.is_a?(Mongoid::Association::Embedded::EmbeddedIn)
72 | c[key] = Reflection.new(meta)
73 | end
74 | c
75 | end
76 |
77 | def bhf_attribute_method?(column_name)
78 | attribute_method?(column_name)
79 | end
80 |
81 | def bhf_primary_key
82 | '_id'
83 | end
84 |
85 | def order(a)
86 | order_by(a)
87 | end
88 |
89 | def reorder(a)
90 | order_by(a)
91 | end
92 |
93 | def bhf_default_search(search_params)
94 | return self if (search_term = search_params['text']).blank?
95 |
96 | arr = []
97 | columns_hash.each_pair do |column, props|
98 | is_number = search_term.to_i.to_s == search_term || search_term.to_f.to_s == search_term
99 |
100 | if props.type == :primary_key
101 | arr << {props.name.to_sym => search_term}
102 | elsif props.type == :string || props.type == :text
103 | arr << {props.name.to_sym => /#{search_term}/i}
104 | elsif props.type == :integer && is_number
105 | arr << {props.name.to_sym => search_term.to_i}
106 | elsif props.type == :float && is_number
107 | arr << {props.name.to_sym => search_term.to_f}
108 | end
109 |
110 | end
111 | self.or(arr)
112 | end
113 |
114 | def get_embedded_parent(parent_id, &block)
115 | relations.each do |key, meta|
116 | next unless meta.is_a?(Mongoid::Association::Embedded::EmbeddedIn)
117 | parent = meta.class_name.constantize
118 | parent = parent.find(parent_id) rescue nil
119 |
120 | if parent
121 | return block.call(parent, meta) if block_given?
122 | return parent
123 | end
124 | end
125 | end
126 |
127 | def bhf_new_embed(parent_id, params = nil)
128 | get_embedded_parent parent_id do |parent, meta|
129 | key_name = if meta.inverse_of?
130 | meta.inverse_of
131 | else
132 | meta.inverse_foreign_key.pluralize
133 | end.to_s
134 | if parent.relations[key_name] and parent.relations[key_name].is_a?(Mongoid::Association::Embedded::EmbedsOne)
135 | parent.send("build_#{key_name}", params)
136 | else
137 | parent.send(key_name).build(params)
138 | end
139 | end
140 | end
141 |
142 | def bhf_find_embed(parent_id, ref_id)
143 | get_embedded_parent parent_id do |parent, meta|
144 | key_name = if meta.inverse_of?
145 | meta.inverse_of
146 | else
147 | meta.inverse_foreign_key.pluralize
148 | end.to_s
149 | relation = parent.send(key_name)
150 | if parent.relations[key_name].is_a?(Mongoid::Association::Embedded::EmbedsOne)
151 | relation
152 | else
153 | relation.find(ref_id)
154 | end
155 | end
156 | end
157 |
158 | def bhf_embedded?
159 | embedded?
160 | end
161 | end
162 |
163 | end
164 | end
165 |
--------------------------------------------------------------------------------
/lib/bhf/platform/attribute/abstract.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Platform::Attribute
2 | class Abstract
3 |
4 | attr_reader :name, :title, :info
5 |
6 | def initialize(options)
7 | @name = options[:name]
8 | @title = options[:title]
9 | @info = options[:info]
10 |
11 | @options_form_type = options[:form_type].to_sym if options[:form_type]
12 | @options_display_type = options[:display_type].to_sym if options[:display_type]
13 | @options_show_type = options[:show_type].to_sym if options[:show_type]
14 |
15 | @link_platform_settings = options[:link] unless options[:link].blank?
16 | end
17 |
18 | def macro
19 | :column
20 | end
21 |
22 | def form_type
23 | @options_form_type || @name
24 | end
25 |
26 | def display_type
27 | @options_display_type || @name
28 | end
29 |
30 | def type_ignore_emtpy?
31 | false
32 | end
33 |
34 | def show_type
35 | @options_show_type || display_type || @name
36 | end
37 |
38 | def link
39 | return unless @link_platform_settings
40 | @link ||= Bhf::Platform::Base.new(@link_platform_settings)
41 | end
42 |
43 | def reflection
44 | false
45 | end
46 |
47 | def db_name
48 | name
49 | end
50 |
51 | def reorderble
52 | false
53 | end
54 |
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/bhf/platform/attribute/column.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Platform::Attribute
2 | class Column
3 |
4 | attr_reader :name, :title, :info, :type
5 |
6 | def initialize(props, options = {})
7 | @name = props.name
8 | @title = options[:title]
9 | @info = options[:info]
10 | @type = props.type
11 |
12 | @options_form_type = options[:form_type].to_sym if options[:form_type]
13 | @options_display_type = options[:display_type].to_sym if options[:display_type]
14 | @options_show_type = options[:show_type].to_sym if options[:show_type]
15 |
16 | @pk = options[:primary_key]
17 | end
18 |
19 | def macro
20 | :column
21 | end
22 |
23 | def form_type
24 | return @options_form_type if @options_form_type
25 |
26 | if name == @pk || name == 'updated_at' || name == 'created_at'
27 | :static
28 | elsif name == 'type'
29 | :type
30 | else
31 | supported_types(@type)
32 | end
33 | end
34 |
35 | def display_type
36 | return @options_display_type if @options_display_type
37 |
38 | if name == @pk
39 | :primary_key
40 | elsif name == 'type'
41 | :type
42 | else
43 | supported_types(@type)
44 | end
45 | end
46 |
47 | def type_ignore_emtpy?
48 | display_type == :boolean || display_type == :toggle
49 | end
50 |
51 | def show_type
52 | @options_show_type || display_type
53 | end
54 |
55 | def reflection
56 | false
57 | end
58 |
59 | def db_name
60 | name
61 | end
62 |
63 | def reorderble
64 | true
65 | end
66 |
67 | private
68 |
69 | def supported_types(check_type)
70 | if [:boolean, :toggle, :text, :array, :hash].include?(check_type)
71 | check_type
72 | elsif type_sym = group_types(check_type)
73 | type_sym
74 | else
75 | :string
76 | end
77 | end
78 |
79 | def group_types(type_sym)
80 | return :date if [:date, :datetime, :timestamp, :time, :year].include?(type_sym)
81 | return :number if [:integer, :float].include?(type_sym)
82 | return :file if type_sym == :file
83 | end
84 |
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/lib/bhf/platform/attribute/reflection.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Platform::Attribute
2 | class Reflection
3 |
4 | attr_reader :name, :title, :info
5 |
6 | def initialize(reflection, options = {})
7 | @name = reflection.name.to_s
8 | @title = options[:title]
9 | @info = options[:info]
10 | @reflection = reflection
11 |
12 | @options_form_type = options[:form_type].to_sym if options[:form_type]
13 | @options_display_type = options[:display_type].to_sym if options[:display_type]
14 | @options_show_type = options[:show_type].to_sym if options[:show_type]
15 |
16 | @link_platform_settings = options[:link] unless options[:link].blank?
17 |
18 | @reorderble = options[:reorderble]
19 | end
20 |
21 | def macro
22 | return :has_and_belongs_to_many if @reflection.macro == :has_many && @reflection.options[:through]
23 | @reflection.macro
24 | end
25 |
26 | def form_type
27 | type
28 | end
29 |
30 | def display_type
31 | @options_display_type || :default
32 | end
33 |
34 | def type_ignore_emtpy?
35 | false
36 | end
37 |
38 | def show_type
39 | @options_show_type || display_type
40 | end
41 |
42 | def type
43 | return @options_form_type if @options_form_type
44 |
45 | if macro == :has_and_belongs_to_many or macro == :has_many
46 | :check_box
47 | elsif macro == :belongs_to or macro == :has_one
48 | :select
49 | else
50 | :static
51 | end
52 | end
53 |
54 | def reflection
55 | @reflection
56 | end
57 |
58 | def db_name
59 | @reflection.foreign_key
60 | end
61 |
62 | def reorderble
63 | @reorderble
64 | end
65 |
66 | def link
67 | return unless @link_platform_settings
68 | @link ||= Bhf::Platform::Base.new(@link_platform_settings)
69 | end
70 |
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/lib/bhf/platform/base.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Platform
2 | class Base
3 | attr_reader :name, :objects, :page_name, :title, :title_zero, :title_singular
4 |
5 | def initialize(options)
6 | @objects = []
7 |
8 | @name = options.name
9 | @data = options.hash
10 | @settings = options.settings_base
11 | @user = @settings.user
12 |
13 | t_model_path = "activerecord.models.#{model.model_name.to_s.downcase}"
14 | model_name = I18n.t(t_model_path, count: 2, default: @name.pluralize.capitalize)
15 | @title = I18n.t("bhf.platforms.#{@name}.title", count: 2, default: model_name)
16 | model_name = I18n.t(t_model_path, count: 1, default: @name.singularize.capitalize)
17 | @title_singular = I18n.t("bhf.platforms.#{@name}.title", count: 1, default: model_name)
18 | model_name = I18n.t(t_model_path, count: 0, default: @name.singularize.capitalize)
19 | @title_zero = I18n.t("bhf.platforms.#{@name}.title", count: 0, default: model_name)
20 |
21 | @page_name = options.page_name
22 | end
23 |
24 |
25 | def pagination
26 | @pagination ||= Bhf::Platform::Pagination.new(entries_per_page)
27 | end
28 |
29 | def get_objects(options = {}, paginate_options = nil)
30 | if user_scope? and @user
31 | chain = @user.send(table_value(:user_scope).to_sym)
32 | else
33 | chain = model
34 | if options[:scope].present?
35 | chain = chain.send options[:scope]
36 | elsif data_source
37 | chain = chain.send data_source
38 | end
39 | end
40 |
41 | if options[:order].present? and options[:direction].present?
42 | chain = chain.reorder("#{options[:order]} #{options[:direction]}")
43 | end
44 |
45 | if search? && options[:search].present?
46 | chain = do_search(chain, options[:search])
47 | end
48 |
49 | if paginate_options && !sortable
50 | chain = chain.page(paginate_options[:page]).per(paginate_options[:per_page])
51 | elsif chain == model
52 | chain = chain.all
53 | end
54 |
55 | @objects = chain
56 | end
57 |
58 | def model
59 | @model ||= if @data['model']
60 | @data['model']
61 | else
62 | @name
63 | end.classify.constantize
64 | end
65 |
66 | def model_name
67 | ActiveModel::Naming.singular(model)
68 | end
69 |
70 | def table_hide?
71 | table_value(:hide) == true || model.bhf_embedded?
72 | end
73 |
74 | def remove_excludes(attrs, excludes)
75 | return attrs unless excludes
76 | attrs.each_with_object([]) do |attribute, obj|
77 | next if excludes.include?(attribute.name.to_s)
78 | obj << attribute
79 | end
80 | end
81 |
82 | def fields
83 | return @fields if @fields
84 |
85 | tmp = default_attrs(form_value(:display), attributes)
86 | if sortable and ! form_value(:display)
87 | tmp = remove_excludes(tmp, [sortable_property.to_s])
88 | end
89 |
90 | @fields = remove_excludes(tmp, form_value(:exclude))
91 | end
92 |
93 | def columns
94 | return @columns if @columns
95 |
96 | tmp = default_attrs(table_columns, attributes[0..5])
97 | if sortable and ! table_columns
98 | tmp = remove_excludes(tmp, [sortable_property.to_s])
99 | end
100 |
101 | @columns = remove_excludes(tmp, table_value(:exclude))
102 | end
103 |
104 | def definitions
105 | return @definitions if @definitions
106 |
107 | tmp = default_attrs(show_value(:display) || show_value(:definitions), attributes)
108 | @definitions = remove_excludes(tmp, show_value(:exclude))
109 | end
110 |
111 | def has_file_upload?
112 | return true if form_value(:multipart) == true
113 |
114 | fields.each do |field|
115 | return true if field.form_type == :file
116 | end
117 | false
118 | end
119 |
120 |
121 |
122 |
123 |
124 |
125 | def search?
126 | table_value(:search) != false
127 | end
128 |
129 | def search_field?
130 | table_value(:search_field) != false
131 | end
132 |
133 | def custom_search
134 | table_value(:custom_search)
135 | end
136 |
137 | def custom_footer
138 | table_value(:custom_footer)
139 | end
140 |
141 | def table_columns
142 | table_value(:display) || table_value(:columns)
143 | end
144 |
145 | def custom_columns?
146 | table_columns.is_a?(Array)
147 | end
148 |
149 | def user_scope?
150 | @user && table_value(:user_scope)
151 | end
152 |
153 | def table
154 | @data['table']
155 | end
156 |
157 | def show
158 | @data['show']
159 | end
160 |
161 | def form
162 | @data['form']
163 | end
164 |
165 | def hooks(method)
166 | if @data['hooks'] && @data['hooks'][method.to_s]
167 | @data['hooks'][method.to_s].to_sym
168 | end
169 | end
170 |
171 | def sortable
172 | table_value 'sortable'
173 | end
174 |
175 | def sortable_property
176 | (@data['sortable_property'] || :position).to_sym
177 | end
178 |
179 | def columns_count
180 | columns.count + (sortable ? 2 : 1)
181 | end
182 |
183 | def hide_edit
184 | table_value 'hide_edit'
185 | end
186 |
187 | def hide_create
188 | table_value('hide_create') || table_value('hide_new') || table_value('hide_add')
189 | end
190 |
191 | def show_duplicate
192 | table_value 'show_duplicate'
193 | end
194 |
195 | def hide_delete
196 | table_value('hide_delete') || table_value('hide_destroy')
197 | end
198 |
199 | def custom_link
200 | table_value 'custom_link'
201 | end
202 |
203 | def table_quick_edit
204 | table_value 'quick_edit'
205 | end
206 |
207 | def custom_partial
208 | table_value 'partial'
209 | end
210 |
211 | def read_data_source
212 | table_value(:source) || table_value(:scope)
213 | end
214 |
215 | def data_source
216 | temp_scope = read_data_source
217 | return unless temp_scope
218 | return temp_scope[0] if temp_scope.is_a?(Array)
219 | temp_scope
220 | end
221 |
222 | def scopes
223 | temp_scope = read_data_source
224 | return unless temp_scope.is_a?(Array) and temp_scope.count > 1
225 | temp_scope.each_with_object([]) do |s, array|
226 | array << OpenStruct.new(
227 | name: I18n.t("bhf.platforms.#{@name}.scopes.#{s}", default: s),
228 | value: s
229 | )
230 | end
231 | end
232 |
233 | def show_extra_fields
234 | show_value(:extra_fields)
235 | end
236 |
237 | def entries_per_page
238 | table_value(:per_page)
239 | end
240 |
241 |
242 |
243 |
244 | private
245 |
246 | def do_search(chain, search_params)
247 | if table_value(:search)
248 | chain.send table_value(:search), search_params
249 | else
250 | chain.bhf_default_search(search_params)
251 | end
252 | end
253 |
254 | def find_platform_settings_for_link(link_name)
255 | if form_value(:links, link_name) != false
256 | if form_value(:links, link_name)
257 | @settings.find_platform_settings(form_value(:links, link_name))
258 | else
259 | @settings.find_platform_settings(link_name.to_s.pluralize) || @settings.find_platform_settings(link_name.to_s.singularize)
260 | end
261 | end
262 | end
263 |
264 | def default_attrs(attrs, d_attrs)
265 | return d_attrs unless attrs
266 |
267 | attrs.each_with_object([]) do |name, obj|
268 | obj << (
269 | attributes.select{ |field| name == field.name }[0] ||
270 | Bhf::Platform::Attribute::Abstract.new(default_attribute_options(name).merge({
271 | name: name,
272 | link: find_platform_settings_for_link(name)
273 | }))
274 | )
275 | end
276 | end
277 |
278 | def attributes
279 | return @attributes if @attributes
280 |
281 | all = {}
282 |
283 | model.columns_hash.each_pair do |name, props|
284 | next if sortable && name == sortable_property
285 | all[name] = Bhf::Platform::Attribute::Column.new(props, default_attribute_options(name).merge({
286 | primary_key: model.bhf_primary_key
287 | }))
288 | end
289 |
290 | model.reflections.each_pair do |name, props|
291 | fk = props.foreign_key
292 | all[name] = Bhf::Platform::Attribute::Reflection.new(props, default_attribute_options(name).merge({
293 | link: find_platform_settings_for_link(name),
294 | reorderble: model.bhf_attribute_method?(fk)
295 | }))
296 |
297 | if all.has_key?(fk) and fk != name.to_s
298 | all.delete(fk)
299 | end
300 | end
301 |
302 | @attributes = default_sort(all)
303 | end
304 |
305 | def default_sort(attrs)
306 | id = []
307 | headlines = []
308 | static_dates = []
309 | output = []
310 |
311 | attrs.each_pair do |key, value|
312 | if key == model.bhf_primary_key
313 | id << value
314 | elsif key == 'title' || key == 'name' || key == 'headline'
315 | headlines << value
316 | elsif key == 'created_at' || key == 'updated_at'
317 | static_dates << value
318 | else
319 | output << value
320 | end
321 | end
322 |
323 | id + headlines + output.sort_by(&:name) + static_dates
324 | end
325 |
326 | def default_attribute_options(name)
327 | {
328 | title: model.human_attribute_name(name),
329 | form_type: form_value(:types, name),
330 | display_type: table_value(:types, name),
331 | show_type: show_value(:types, name),
332 | info: I18n.t("bhf.platforms.#{@name}.infos.#{name}", default: '')
333 | }
334 | end
335 |
336 |
337 |
338 | def form_value(key, attribute = nil)
339 | lookup_value form, key, attribute
340 | end
341 |
342 | def table_value(key, attribute = nil)
343 | lookup_value table, key, attribute
344 | end
345 |
346 | def show_value(key, attribute = nil)
347 | lookup_value show, key, attribute
348 | end
349 |
350 | def lookup_value(main_key, key, attribute = nil)
351 | if main_key
352 | if attribute == nil
353 | main_key[key.to_s]
354 | elsif main_key[key.to_s]
355 | main_key[key.to_s][attribute.to_s]
356 | end
357 | end
358 | end
359 |
360 | end
361 | end
362 |
--------------------------------------------------------------------------------
/lib/bhf/platform/pagination.rb:
--------------------------------------------------------------------------------
1 | Kaminari::Helpers::Tag.class_eval do
2 | def page_url_for(page)
3 | if @param_name.is_a?(Array)
4 | @params[@param_name[0]] = (@params[@param_name[0]] || {}).merge(@param_name[1] => (page <= 1 ? 1 : page))
5 | return @template.url_for @params
6 | end
7 |
8 | @template.url_for @params.merge(@param_name => (page <= 1 ? 1 : page))
9 | end
10 | end
11 | # TODO: monitor https://github.com/amatsuda/kaminari/issues/542#issuecomment-40294388
12 |
13 | module Bhf::Platform
14 |
15 | class Pagination
16 |
17 | attr_accessor :template
18 | attr_reader :offset_per_page, :offset_to_add
19 |
20 | def initialize(offset_per_page = 10, offset_to_add = 5)
21 | @offset_per_page = offset_per_page || 10
22 | @offset_to_add = offset_to_add
23 | end
24 |
25 | def create(platform)
26 | platform_params = template.params[platform.name] || {}
27 |
28 | links = if !(page_links = template.paginate(platform.objects,
29 | theme: 'bhf',
30 | param_name: [platform.name, :page],
31 | params: template.params.permit!
32 | )).blank?
33 | "#{load_more(platform)} #{page_links}"
34 | elsif platform.objects.total_pages == 1 && platform.objects.size > @offset_to_add
35 | load_less(platform)
36 | end
37 |
38 | if links
39 | template.content_tag(:div, links.html_safe, {class: 'pagination'})
40 | end
41 | end
42 |
43 | def info(platform, options = {})
44 | collection = platform.objects
45 |
46 | if collection.respond_to?(:num_pages) and collection.num_pages > 1
47 | I18n.t('bhf.pagination.info.default', {
48 | name: platform.title,
49 | count: collection.total_count,
50 | offset_start: collection.offset_value + 1,
51 | offset_end: collection.offset_value + collection.limit_value
52 | })
53 | else
54 | count = collection.size
55 | platform_title = case count
56 | when 0
57 | platform.title
58 | when 1
59 | platform.title_singular
60 | else
61 | platform.title
62 | end
63 |
64 | I18n.t('bhf.pagination.info',
65 | count: count,
66 | name: platform_title
67 | )
68 | end.html_safe
69 | end
70 |
71 | def load_more(platform, attributes = {}, plus = true)
72 | platform_params = template.params[platform.name] || {}
73 | load_offset = @offset_per_page
74 | load_offset = platform_params[:per_page].to_i if platform_params[:per_page]
75 | if plus
76 | load_offset += @offset_to_add
77 | else
78 | load_offset -= @offset_to_add
79 | end
80 |
81 | platform_params.delete(:page)
82 | platform_params[:per_page] = load_offset
83 |
84 | direction = plus ? 'more' : 'less'
85 |
86 | parsed_paramas = {
87 | bhf_area: template.params[:bhf_area],
88 | page: template.params[:page]
89 | }.merge(platform.name.to_sym => platform_params)
90 | template.link_to(
91 | I18n.t("bhf.pagination.load_#{direction}"),
92 | template.page_path(platform.page_name, parsed_paramas),
93 | attributes.merge(class: "load_#{direction}")
94 | )
95 | end
96 |
97 | def load_less(platform, attributes = {})
98 | load_more(platform, attributes, false)
99 | end
100 |
101 | end
102 | end
103 |
--------------------------------------------------------------------------------
/lib/bhf/settings/base.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Settings
2 |
3 | class Base
4 |
5 | attr_accessor :user
6 |
7 | def initialize(settings_hash, user = nil)
8 | @settings_hash = settings_hash
9 | @user = user
10 |
11 | t = pages.each_with_object([]) do |page, obj|
12 | content_for_page(page).each do |platform|
13 | obj << platform.keys.flatten
14 | end
15 | end.flatten!
16 | if t.nil?
17 | raise Exception.new("No bhf pages found")
18 | end
19 | if t.uniq.length != t.length
20 | raise Exception.new("Platforms with identical names: '#{t.detect{ |e| t.count(e) > 1 }}'")
21 | end
22 | end
23 |
24 | def pages
25 | @pages ||= @settings_hash['pages'].each_with_object([]) do |page, obj|
26 | if page.is_a?(String)
27 | page = {page => nil}
28 | end
29 | obj << page.keys[0]
30 | end
31 | end
32 |
33 | def content_for_page(selected_page)
34 | @settings_hash['pages'].each do |page|
35 | page = {page => nil} if page.is_a?(String)
36 |
37 | if selected_page == page.keys[0]
38 | return page.values.flatten
39 | end
40 | end
41 | nil
42 | end
43 |
44 | def find_platform_settings(platform_name)
45 | pages.each do |page|
46 | content_for_page(page).each do |platform_hash|
47 | if platform_hash.keys[0] == platform_name.to_s
48 | return Bhf::Settings::Platform.new(platform_hash, page, self)
49 | end
50 | end
51 | end
52 | nil
53 | end
54 |
55 | end
56 |
57 | end
58 |
--------------------------------------------------------------------------------
/lib/bhf/settings/platform.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Settings
2 |
3 | class Platform
4 |
5 | attr_reader :name, :data, :page_name, :hash, :settings_base
6 |
7 | def initialize(settings_hash, page_name, settings_base)
8 | if settings_hash.is_a?(String)
9 | settings_hash = {settings_hash => nil}
10 | end
11 | @name = settings_hash.keys[0]
12 | @hash = settings_hash.values[0] || {}
13 |
14 | @settings_base = settings_base
15 | @page_name = page_name
16 | end
17 |
18 | end
19 |
20 | end
21 |
--------------------------------------------------------------------------------
/lib/bhf/settings/yaml_parser.rb:
--------------------------------------------------------------------------------
1 | module Bhf::Settings
2 |
3 | class YAMLParser
4 |
5 | attr_accessor :settings_hash
6 |
7 | def initialize(roles_array, area = nil)
8 | roles_settings = roles_yml(roles_array, area)
9 |
10 | if Bhf.configuration.abstract_settings.any?
11 | tmp_pages = get_settings_array(Bhf.configuration.abstract_settings)['pages']
12 | abstract_platform_settings = tmp_pages.each_with_object({}) do |abstract_pages, hash|
13 | abstract_pages.each do |abstract_page|
14 | abstract_page[1].each do |abstract_platform|
15 | hash.merge!(abstract_platform)
16 | end
17 | end
18 | end
19 |
20 | roles_settings['pages'].each_with_index do |pages, i_1|
21 | pages.each_pair do |key_1, page|
22 | page.each_with_index do |platform, i_2|
23 | platform.each_pair do |key_2, v|
24 | value = v.is_a?(Hash) ? v : {}
25 | abstract_platform_key = if value['extend_abstract']
26 | value['extend_abstract']
27 | elsif abstract_platform_settings[key_2]
28 | key_2
29 | end
30 | next unless abstract_platform_key
31 | roles_settings['pages'][i_1][key_1][i_2][key_2] = abstract_platform_settings[abstract_platform_key].deep_merge(value)
32 | end
33 | end
34 | end
35 | end
36 | end
37 |
38 | @settings_hash = roles_settings
39 | end
40 |
41 | def get_settings_array(array, dir = nil)
42 | array.each_with_object({'pages' => []}) do |file_name, tmp_settings_hash|
43 | absolut_path = false
44 | file_path = if dir
45 | "#{dir}/#{file_name}"
46 | else
47 | if file_name[0,1] == '/'
48 | absolut_path = true
49 | file_name
50 | else
51 | "/#{file_name}"
52 | end
53 | end
54 | pages = load_yml(file_path, absolut_path)['pages']
55 | tmp_settings_hash['pages'] += pages if pages
56 | end
57 | end
58 |
59 | def roles_yml(roles = nil, area = nil)
60 | area_dir = "/#{area}" if area.present?
61 | if roles.is_a?(Array)
62 | files = get_settings_array(roles, area_dir)
63 |
64 | # find the same pages and merge them
65 | merged_files = {'pages' => []}
66 | files['pages'].each do |pages|
67 | merged = false
68 | pages.each_pair do |key, page|
69 | merged_files['pages'].each do |m_page|
70 | if m_page.include?(key)
71 | merged = true
72 | m_page[key] = m_page[key] + page
73 | end
74 | end
75 | end
76 | unless merged
77 | merged_files['pages'] << pages
78 | end
79 | end
80 | merged_files
81 | else
82 | load_yml(area.present? ? "/#{area}/bhf" : nil)
83 | end
84 | end
85 |
86 | def load_yml(suffix = nil, absolut_path = false)
87 | file_path = if absolut_path
88 | "#{suffix}.yml"
89 | else
90 | "config/bhf#{suffix}.yml"
91 | end
92 | YAML::load(IO.read(file_path))
93 | end
94 |
95 | end
96 |
97 | end
98 |
--------------------------------------------------------------------------------
/lib/rails/generators/bhf/templates/initializer.rb:
--------------------------------------------------------------------------------
1 | Bhf.configure do |config|
2 | # config.css << 'bhf'
3 | # config.js << 'bhf'
4 | # config.abstract_settings << 'abstract'
5 |
6 | # config.logo = lambda { |area| 'my_logo.svg' }
7 | # config.on_login_fail = :root_url
8 | # config.logout_path = :logout_path
9 |
10 | # config.session_account_id = :admin_account_id
11 | # config.session_auth_name = :is_admin
12 | # config.account_model = 'User'
13 | # config.account_model_find_method = 'find'
14 | end
15 |
--------------------------------------------------------------------------------
/test/database.yml:
--------------------------------------------------------------------------------
1 | mysql:
2 | adapter: mysql
3 | host: 127.0.0.1
4 | database: bhf_test
5 | username: root
6 | password:
7 | encoding: utf8
8 | pool: 5
9 | timeout: 5000
10 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV["RAILS_ENV"] = "test"
2 |
3 | require 'test/unit'
4 | require 'rubygems'
5 | require 'yaml'
6 | require 'active_record'
7 | require 'mysql'
8 |
9 | require 'app/models/bhf/widget.rb'
10 |
11 | def bhf_widget( fixture_name )
12 | id = @@fixtures['bhf_widget'][ fixture_name.to_s ][ 'id' ]
13 | Bhf::Widget.find( id )
14 | end
15 |
16 | def load_schema
17 | config = YAML::load( IO.read( File.dirname(__FILE__) + '/database.yml') )
18 |
19 | # Manually initialize the database
20 | conn = Mysql.real_connect( config['mysql']['host'], config['mysql']['username'], config['mysql']['password'] )
21 | conn.query( "CREATE DATABASE IF NOT EXISTS #{config['mysql']['database']}" )
22 |
23 | ActiveRecord::Base.establish_connection( config['mysql'] )
24 | ActiveRecord::Base.connection()
25 |
26 | load(File.dirname(__FILE__) + "/../lib/rails/generators/bhf/templates/schema.rb")
27 |
28 | @@fixtures = {}
29 |
30 | load_fixture( 'bhf_widget' )
31 | end
32 |
33 | def load_fixture( table )
34 | @@fixtures[ table ] = {}
35 | fixture = YAML::load( IO.read( File.dirname(__FILE__) + "/fixtures/#{table}.yml") )
36 | @@fixtures[ table ] = fixture
37 |
38 | klass = class_eval table.titleize.gsub(/ /, '::')
39 |
40 | fixture.each do |record_name, record|
41 | record.each do |column, value|
42 | if ( match = column.match(/(.*)_id/) )
43 | fixture_reference = "bhf_" + match[1].pluralize
44 | if value.is_a? Symbol
45 | r = class_eval "#{fixture_reference}( '#{value}' )"
46 | record[ column ] = r.id
47 | end
48 | end
49 | end
50 |
51 | r = klass.create( record )
52 | @@fixtures[ table ][ record_name ][ 'id' ] = r.id
53 | end
54 |
55 | end
56 |
--------------------------------------------------------------------------------