character 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -1
- data/.rspec +1 -0
- data/README.md +185 -14
- data/Rakefile +8 -1
- data/app/assets/images/character/logo.jpg +0 -0
- data/app/assets/javascripts/character.coffee +134 -0
- data/app/assets/javascripts/character/dashboard/_visitors.coffee +27 -0
- data/app/assets/javascripts/character/dashboard/layout.coffee +156 -0
- data/app/assets/javascripts/character/dashboard/module.coffee +51 -0
- data/app/assets/javascripts/character/generic/details.coffee +233 -0
- data/app/assets/javascripts/character/generic/helpers/compact_object.coffee +7 -0
- data/app/assets/javascripts/character/generic/helpers/data_inputs.coffee +21 -0
- data/app/assets/javascripts/character/generic/helpers/date_select.coffee +45 -0
- data/app/assets/javascripts/character/generic/helpers/editor.coffee +11 -0
- data/app/assets/javascripts/character/generic/helpers/redactor.coffee +38 -0
- data/app/assets/javascripts/character/generic/helpers/reorder.coffee +36 -0
- data/app/assets/javascripts/character/generic/layout.coffee +40 -0
- data/app/assets/javascripts/character/generic/list.coffee +214 -0
- data/app/assets/javascripts/character/generic/model.coffee +135 -0
- data/app/assets/javascripts/character/generic/module.coffee +157 -0
- data/app/assets/javascripts/character/images/module.coffee +148 -0
- data/app/assets/javascripts/character/pages/module.coffee +43 -0
- data/app/assets/javascripts/character/posts/module.coffee +113 -0
- data/app/assets/javascripts/character/settings/_admins.coffee +61 -0
- data/app/assets/javascripts/character/settings/_authors.coffee +56 -0
- data/app/assets/javascripts/character/settings/_categories.coffee +61 -0
- data/app/assets/javascripts/character/settings/_layout.coffee +7 -0
- data/app/assets/javascripts/character/settings/_redirects.coffee +56 -0
- data/app/assets/javascripts/character/settings/_website.coffee +7 -0
- data/app/assets/javascripts/character/settings/details.coffee +16 -0
- data/app/assets/javascripts/character/settings/layout.coffee +46 -0
- data/app/assets/javascripts/character/settings/module.coffee +78 -0
- data/app/assets/stylesheets/character.scss +37 -0
- data/app/assets/stylesheets/character/_admins.scss +30 -0
- data/app/assets/stylesheets/character/_authors.scss +30 -0
- data/app/assets/stylesheets/character/_categories.scss +32 -0
- data/app/assets/stylesheets/character/_dashboard.scss +143 -0
- data/app/assets/stylesheets/character/_posts.scss +93 -0
- data/app/assets/stylesheets/character/_redirects.scss +35 -0
- data/app/assets/stylesheets/character/base.scss +967 -0
- data/app/assets/stylesheets/character/typography.scss +29 -0
- data/app/controllers/character/api_controller.rb +170 -0
- data/app/controllers/character/application_controller.rb +37 -0
- data/app/controllers/character/settings_controller.rb +72 -0
- data/app/controllers/concerns/character/auth_concern.rb +41 -0
- data/app/controllers/concerns/character/instance_concern.rb +31 -0
- data/app/controllers/concerns/character/json_object_concern.rb +32 -0
- data/app/controllers/concerns/character/model_class_concern.rb +28 -0
- data/app/controllers/concerns/character/params_concern.rb +33 -0
- data/app/controllers/concerns/character/templates_concern.rb +32 -0
- data/app/controllers/concerns/not_found.rb +18 -0
- data/app/controllers/concerns/website_settings.rb +18 -0
- data/app/controllers/pages_controller.rb +8 -0
- data/app/controllers/posts_controller.rb +43 -0
- data/app/helpers/character_helper.rb +8 -0
- data/app/helpers/page_helper.rb +67 -0
- data/app/inputs/foundation_string_input.rb +44 -0
- data/app/inputs/foundation_switch_input.rb +35 -0
- data/app/models/character/image.rb +12 -0
- data/app/models/character/page.rb +21 -0
- data/app/models/character/post.rb +32 -12
- data/app/models/character/post_author.rb +22 -0
- data/app/models/character/post_category.rb +21 -0
- data/app/models/character/redirect.rb +15 -0
- data/app/models/character/settings/variable.rb +23 -0
- data/app/models/character/sitemap/sitemap_generator_helper.rb +15 -0
- data/app/models/character/user.rb +29 -0
- data/app/models/concerns/created_ago.rb +12 -0
- data/app/models/concerns/hideable.rb +27 -0
- data/app/models/concerns/orderable.rb +8 -0
- data/app/models/concerns/report.rb +11 -0
- data/app/models/concerns/report_daily.rb +32 -0
- data/app/models/concerns/report_monthly.rb +18 -0
- data/app/models/concerns/report_weekly.rb +19 -0
- data/app/models/concerns/updated_ago.rb +12 -0
- data/app/models/reports/analytics_daily.rb +26 -0
- data/app/models/reports/analytics_monthly.rb +16 -0
- data/app/models/reports/analytics_weekly.rb +16 -0
- data/app/services/google_analytics.rb +43 -0
- data/app/uploaders/character/image_uploader.rb +22 -0
- data/app/uploaders/character/settings/file_uploader.rb +5 -0
- data/app/views/character/character.html.erb +67 -0
- data/app/views/character/generic/form.html.erb +8 -0
- data/app/views/character/pages/form.html.erb +28 -0
- data/app/views/character/posts/form.html.erb +38 -0
- data/app/views/character/settings/admins.html.erb +29 -0
- data/app/views/character/settings/post_authors.html.erb +28 -0
- data/app/views/character/settings/post_categories.html.erb +31 -0
- data/app/views/character/settings/redirects.html.erb +30 -0
- data/app/views/character/settings/settings_group.html.erb +67 -0
- data/app/views/errors/not_found.html.erb +157 -0
- data/app/views/pages/_default.html.erb +3 -0
- data/app/views/pages/_redactor.html.erb +3 -0
- data/app/views/pages/show.html.erb +5 -0
- data/app/views/posts/_post.html.erb +17 -0
- data/app/views/posts/author.html.erb +18 -0
- data/app/views/posts/category.html.erb +18 -0
- data/app/views/posts/index.html.erb +18 -0
- data/app/views/posts/rss.builder +19 -0
- data/app/views/posts/show.html.erb +14 -0
- data/app/views/shared/_google_analytics.html.erb +13 -0
- data/character.gemspec +48 -5
- data/doc/README_old.md +161 -0
- data/doc/generic_app.md +19 -0
- data/doc/img/demo-1.jpg +0 -0
- data/doc/img/demo-2.jpg +0 -0
- data/doc/img/demo-3.jpg +0 -0
- data/doc/img/demo-4.jpg +0 -0
- data/doc/img/demo-5.jpg +0 -0
- data/doc/instances.md +39 -0
- data/doc/settings.md +1 -0
- data/lib/character.rb +29 -1
- data/lib/character/engine.rb +33 -1
- data/lib/character/generators/bootstrap_generator.rb +51 -0
- data/lib/character/instance.rb +59 -0
- data/lib/character/routing.rb +42 -5
- data/lib/character/settings.rb +101 -0
- data/lib/character/templates/admin.coffee +15 -0
- data/lib/character/templates/admin.scss +3 -0
- data/lib/character/templates/application.html.erb +44 -0
- data/lib/character/templates/application.scss +12 -0
- data/lib/character/templates/assets.rb +1 -0
- data/lib/character/templates/initializer.rb +5 -0
- data/lib/character/templates/settings.scss +11 -0
- data/lib/character/templates/settings.yml +67 -0
- data/lib/character/templates/typography.scss +13 -0
- data/lib/character/version.rb +2 -2
- data/lib/mongoid/carrierwave_serialization_patch.rb +9 -0
- data/lib/tasks/analytics.rake +52 -0
- data/test/config/application.rb +65 -0
- data/test/config/mongoid.yml +12 -0
- data/test/config/secrets.yml +22 -0
- data/test/controllers/character/api_controller_test.rb +94 -0
- data/test/factories/product_factory.rb +5 -0
- data/test/lib/character/engine_test.rb +33 -0
- data/test/lib/character/routing_test.rb +31 -0
- data/test/test_helper.rb +48 -0
- data/vendor/assets/javascripts/backbone.js +944 -794
- data/vendor/assets/javascripts/jquery.fileupload.js +1426 -0
- data/vendor/assets/javascripts/jquery.form.js +1278 -0
- data/vendor/assets/javascripts/jquery.iframe-transport.js +214 -0
- data/vendor/assets/javascripts/raphael.js +8117 -0
- data/vendor/assets/javascripts/raphael.morris.js +1885 -0
- data/vendor/assets/javascripts/underscore.inflection.js +177 -0
- data/vendor/assets/javascripts/underscore.string.js +1 -1
- data/vendor/assets/stylesheets/csspinner.css +361 -0
- data/vendor/assets/stylesheets/normalize.css +423 -0
- metadata +499 -49
- data/app/controllers/character/posts_controller.rb +0 -27
- data/lib/generators/character/install_generator.rb +0 -42
- data/lib/generators/character/templates/README +0 -1
- data/lib/generators/character/templates/admin/character.rb +0 -3
- data/vendor/assets/fonts/general_foundicons.eot +0 -0
- data/vendor/assets/fonts/general_foundicons.svg +0 -15
- data/vendor/assets/fonts/general_foundicons.ttf +0 -0
- data/vendor/assets/fonts/general_foundicons.woff +0 -0
- data/vendor/assets/javascripts/character/index.js.coffee +0 -53
- data/vendor/assets/javascripts/character/models/post.js.coffee +0 -39
- data/vendor/assets/javascripts/character/views/app.js.coffee +0 -81
- data/vendor/assets/javascripts/character/views/editor.js.coffee +0 -231
- data/vendor/assets/javascripts/character/views/editor_settings.js.coffee +0 -44
- data/vendor/assets/javascripts/character/views/index.js.coffee +0 -116
- data/vendor/assets/javascripts/character/views/preview.js.coffee +0 -49
- data/vendor/assets/javascripts/jquery.smartresize.js +0 -30
- data/vendor/assets/javascripts/lodash.js +0 -4258
- data/vendor/assets/javascripts/showdown.js +0 -62
- data/vendor/assets/stylesheets/character/_base.css.scss +0 -84
- data/vendor/assets/stylesheets/character/_icons.css.scss.erb +0 -96
- data/vendor/assets/stylesheets/character/_view_editor.css.scss +0 -115
- data/vendor/assets/stylesheets/character/_view_index.css.scss +0 -73
- data/vendor/assets/stylesheets/character/_view_preview.css.scss +0 -49
- data/vendor/assets/stylesheets/character/index.css.scss +0 -32
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 5aaba1245301fea933c7e85fe1784c98dabbafc0
|
|
4
|
+
data.tar.gz: c8db8ace583070cc476269ba4590d355b5578503
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e3cd3b297d2a5df01ca497df51d48c279c2506dfad4651732d257b0d00b8da1fe74bb30144fa3a6befeeae18b5692bb9f659fc8594de9221aadc3042770a15b9
|
|
7
|
+
data.tar.gz: 2423e88b08b927a97d30c68e04a4445a79010939e92e391812bc28eba9dadd0fe8046311f793c2790b1142abc76d826285ef1c4143db4a22fcac4d4cba0177fb
|
data/.gitignore
CHANGED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/README.md
CHANGED
|
@@ -1,20 +1,191 @@
|
|
|
1
|
-
# Character
|
|
1
|
+
# Character Rails Admin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Have you heard of [Django](https://www.djangoproject.com/), [Wordpress](https://wordpress.org/), [Active Admin](http://activeadmin.info/), [Locomotive](http://locomotivecms.com/)? Yeah?! — Those are all awesome... and **Character** is BETTER!
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
|
|
7
|
+
## Setup new [Rails 4.1](http://rubyonrails.org) project
|
|
8
|
+
|
|
9
|
+
rails new ProjectName -T -O
|
|
10
|
+
|
|
11
|
+
Add gems to the ```Gemfile```:
|
|
12
|
+
|
|
13
|
+
# Mongoid ORM + Character
|
|
14
|
+
gem 'carrierwave-mongoid', github: 'carrierwaveuploader/carrierwave-mongoid', require: 'carrierwave/mongoid'
|
|
15
|
+
gem 'character'
|
|
16
|
+
|
|
17
|
+
Run bundle and run generators:
|
|
18
|
+
|
|
19
|
+
bundle ; rails g mongoid:config ; rails g character:bootstrap
|
|
7
20
|
|
|
8
21
|
## TODO
|
|
9
22
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
. Save unpublished post
|
|
24
|
+
. Fix paste code issue for redactor.js + chrome
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Routes
|
|
28
|
+
|
|
29
|
+
After character generator finishes it's dirty business, in ```/config/routes.rb``` you see:
|
|
30
|
+
|
|
31
|
+
mount_character_instance 'admin'
|
|
32
|
+
|
|
33
|
+
This mounts character instance **admin** to ```/admin``` path and make character app accessible there. There are also two optional helpers ```mount_posts_at``` and ```mount_pages_at```, they mount default controllers to routes as well. Remove them if no need in **posts** or **pages** app.
|
|
34
|
+
|
|
35
|
+
Instance name **admin** could be changed, and you can use something different. This option is here for the case when a few character instances are required.
|
|
36
|
+
|
|
37
|
+
- mount_character_instance
|
|
38
|
+
- mount_posts_at
|
|
39
|
+
- mount_pages_at
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Running Automated Tests
|
|
43
|
+
|
|
44
|
+
`$ bundle exec rake test`
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Modules
|
|
48
|
+
|
|
49
|
+
- chr.genericModule()
|
|
50
|
+
- chr.postsModule()
|
|
51
|
+
- chr.pagesModule()
|
|
52
|
+
- chr.settingsModule()
|
|
53
|
+
- chr.settingsWebsite()
|
|
54
|
+
- chr.settingsPostCategories()
|
|
55
|
+
- chr.settingsAdmins()
|
|
56
|
+
- chr.settingsRedirects()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Forms
|
|
60
|
+
|
|
61
|
+
To have custom form implementation for model, create ```form.html``` in ```/app/views/admin/model_names/``` — replace *model_names* with pluralized models name and if needed character instance name *admin* (default).
|
|
62
|
+
|
|
63
|
+
### Generic form template
|
|
64
|
+
|
|
65
|
+
Generic form template looks like this:
|
|
66
|
+
|
|
67
|
+
<%= simple_form_for @object, url: @form_action_url, method: :post do |f| %>
|
|
68
|
+
<%= f.input :name %>
|
|
69
|
+
<% end %>
|
|
70
|
+
|
|
71
|
+
Checkout [Simple Form](https://github.com/plataformatec/simple_form) reference for all options (there are tons of them) which are available here.
|
|
72
|
+
|
|
73
|
+
**No need to include SUBMIT button in form!**
|
|
74
|
+
|
|
75
|
+
### Hideable
|
|
76
|
+
|
|
77
|
+
If you want to make model hideable include ```include Hideable``` in model and add hidden field to your form:
|
|
78
|
+
|
|
79
|
+
<%= f.input :hidden, as: :hidden %>
|
|
80
|
+
|
|
81
|
+
This will add an eye button trigger in the admin header, which allows to switch state for model.
|
|
82
|
+
|
|
83
|
+
### Inline forms
|
|
84
|
+
|
|
85
|
+
Form inline elements could be added with this code (images example):
|
|
86
|
+
|
|
87
|
+
<div class='chr-form-nested chr-form-nested-images sortable-list'>
|
|
88
|
+
<%= f.fields_for :images do |ff| %>
|
|
89
|
+
<%= ff.link_to_remove "Remove" %>
|
|
90
|
+
<%= image_tag ff.object.image.small.url %>
|
|
91
|
+
<%= ff.input :title, placeholder: 'Image title' %>
|
|
92
|
+
<%= ff.input :image %>
|
|
93
|
+
<%= ff.input :_position, as: :hidden %>
|
|
94
|
+
<% end %>
|
|
95
|
+
<%= f.link_to_add "Add an Image", :images %>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
- This template is based on [Nested Forms](https://github.com/ryanb/nested_form) gem by Ryan Bates, checkout docs for implementation details.
|
|
99
|
+
- Including ```sortable-list``` class and ```<%= ff.input :_position, as: :hidden %>``` make inline objects reorderable.
|
|
100
|
+
- This example uses ```chr-form-nested-images``` class for layout styling.
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Models
|
|
104
|
+
|
|
105
|
+
- Character::Post
|
|
106
|
+
- Character::PostCategory
|
|
107
|
+
- Character::Page
|
|
108
|
+
- Character::Image
|
|
109
|
+
- Character::Settings::Variable
|
|
110
|
+
- Character::User
|
|
111
|
+
- Character::Redirect
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
## Concerns
|
|
115
|
+
|
|
116
|
+
- UpdatedAgo
|
|
117
|
+
- CreatedAgo
|
|
118
|
+
- Orderable
|
|
119
|
+
- Hideable
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
## Helpers
|
|
123
|
+
|
|
124
|
+
- Character::SitemapGeneratorHelper
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## Shortcuts
|
|
128
|
+
|
|
129
|
+
- CMD+s — save changes
|
|
130
|
+
- CMD+e — toggle fullscreen
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
## API
|
|
134
|
+
|
|
135
|
+
**[Character.Generic.DetailsView](https://github.com/slate-studio/character/blob/master/app/assets/javascripts/character/generic/details.coffee) and [Character.Settings.DetailsView](https://github.com/slate-studio/character/blob/master/app/assets/javascripts/character/settings/details.coffee)**
|
|
136
|
+
|
|
137
|
+
These are blank methods that could be overriden to extend view functionality. Example of ```Character.Generic.DetailsView``` override: [character/posts/module.coffee](https://github.com/slate-studio/character/blob/master/app/assets/javascripts/character/posts/module.coffee)
|
|
138
|
+
|
|
139
|
+
- @beforeContentRequest()
|
|
140
|
+
- @beforeRenderContent()
|
|
141
|
+
- @beforeFormHelpersStart()
|
|
142
|
+
- @afterRenderContent()
|
|
143
|
+
- @beforeSave()
|
|
144
|
+
- @beforeFormSubmit(arr, $form, options)
|
|
145
|
+
- @afterFormSubmitSuccess(responseText, statusText, xhr, $form)
|
|
146
|
+
- @beforeOnClose()
|
|
147
|
+
- @afterOnClose()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## Instances
|
|
151
|
+
|
|
152
|
+
Initializers for different instances are in ```config/initializers/character.rb```:
|
|
153
|
+
|
|
154
|
+
Character.configure do |config|
|
|
155
|
+
config.title = 'Project Title | Admin'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
Options:
|
|
159
|
+
|
|
160
|
+
- @title = 'Character'
|
|
161
|
+
- @user_model = 'Character::User'
|
|
162
|
+
- @development_auto_login = false
|
|
163
|
+
- @force_ssl = true
|
|
164
|
+
|
|
165
|
+
## Analytics
|
|
166
|
+
|
|
167
|
+
* Create a new project (```GA_APP_NAME```) at: [https://console.developers.google.com/project](https://console.developers.google.com/project)
|
|
168
|
+
* Enable Analytics under **APIs & auth** -> **APIs** -> Analytics API
|
|
169
|
+
* Create another client under **Credentials**
|
|
170
|
+
* Download and put **key-file** (```GA_KEY_FILE_NAME```) to your projects ```config``` folder
|
|
171
|
+
* Go to you (Analytics)[www.google.com/analytics] account and add the **Service account** email address to your account (```GA_SERVICE_ACCOUNT_EMAIL```). It should be something like ```something-long@developer.gserviceaccount.com```.
|
|
172
|
+
|
|
173
|
+
Set all variables in server environment:
|
|
174
|
+
|
|
175
|
+
- ```GA_APP_NAME```
|
|
176
|
+
- ```GA_SERVICE_ACCOUNT_EMAIL```
|
|
177
|
+
- ```GA_KEY_FILE_NAME```
|
|
178
|
+
- ```GA_PROFILE_ID```
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## Settings
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
## Reports
|
|
185
|
+
|
|
186
|
+
(to be continued...)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
## Redirects
|
|
190
|
+
|
|
191
|
+
Redirects app allows to quickly setup 301/302 redirects for the website.
|
data/Rakefile
CHANGED
|
@@ -4,4 +4,11 @@ spec = Gem::Specification.load(Dir['*.gemspec'].first)
|
|
|
4
4
|
gem = Gem::PackageTask.new(spec)
|
|
5
5
|
gem.define()
|
|
6
6
|
|
|
7
|
-
#gem
|
|
7
|
+
# This will let us run our tests by typing "rake test" from the gem root folder
|
|
8
|
+
require 'rake/testtask'
|
|
9
|
+
Rake::TestTask.new do |t|
|
|
10
|
+
t.libs << 'test'
|
|
11
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
12
|
+
# t.warning = true
|
|
13
|
+
# t.verbose = true
|
|
14
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#= require jquery
|
|
2
|
+
#= require jquery_ujs
|
|
3
|
+
#= require jquery.form
|
|
4
|
+
#= require jquery.ui.sortable
|
|
5
|
+
#= require jquery.ui.widget
|
|
6
|
+
#= require jquery.iframe-transport
|
|
7
|
+
#= require jquery.fileupload
|
|
8
|
+
#= require jquery_nested_form
|
|
9
|
+
|
|
10
|
+
#= require underscore
|
|
11
|
+
#= require underscore.string
|
|
12
|
+
#= require underscore.inflection
|
|
13
|
+
#= require backbone
|
|
14
|
+
#= require backbone.marionette
|
|
15
|
+
|
|
16
|
+
#= require moment
|
|
17
|
+
#= require character_editor
|
|
18
|
+
#= require redactor
|
|
19
|
+
|
|
20
|
+
#= require_self
|
|
21
|
+
#= require ./character/generic/module
|
|
22
|
+
#= require ./character/settings/module
|
|
23
|
+
#= require ./character/posts/module
|
|
24
|
+
#= require ./character/images/module
|
|
25
|
+
#= require ./character/pages/module
|
|
26
|
+
#= require ./character/dashboard/module
|
|
27
|
+
|
|
28
|
+
@Character ||= {}
|
|
29
|
+
|
|
30
|
+
_.mixin(_.str.exports())
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Unsuck Your Backbone
|
|
34
|
+
# https://speakerdeck.com/ammeep/unsuck-your-backbone
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Marionette.js Application Documentation
|
|
39
|
+
# https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md
|
|
40
|
+
#
|
|
41
|
+
@chr = new Backbone.Marionette.Application()
|
|
42
|
+
|
|
43
|
+
# App Layout
|
|
44
|
+
# /app/views/character.html.erb
|
|
45
|
+
@chr.addRegions
|
|
46
|
+
menu: '#menu'
|
|
47
|
+
content: '#content'
|
|
48
|
+
|
|
49
|
+
API =
|
|
50
|
+
addMenuItem: (path, icon, title) ->
|
|
51
|
+
$menuItems = $('#menu_items')
|
|
52
|
+
$menuItems.append """<li>
|
|
53
|
+
<a href='#/#{ path }' class='chr-menu-item-#{ path }' title='#{ title }'>
|
|
54
|
+
<i class='chr-menu-icon fa fa-#{ icon }'></i><div class='chr-menu-title'>#{ title }</div>
|
|
55
|
+
</a>
|
|
56
|
+
</li>"""
|
|
57
|
+
|
|
58
|
+
showModule: (module) ->
|
|
59
|
+
if chr.currentModuleName != module.moduleName
|
|
60
|
+
chr.currentModuleName = module.moduleName
|
|
61
|
+
|
|
62
|
+
name = module.moduleName
|
|
63
|
+
layout = module.layout
|
|
64
|
+
|
|
65
|
+
$menuEl = $('#menu')
|
|
66
|
+
$menuEl.find('a.active').removeClass('active')
|
|
67
|
+
$menuEl.find("a.chr-menu-item-#{name}").addClass('active')
|
|
68
|
+
|
|
69
|
+
chr.content.show(layout, { preventDestroy: true })
|
|
70
|
+
|
|
71
|
+
$('#content').attr('class', "chr-content #{name}")
|
|
72
|
+
|
|
73
|
+
closeDetailsView: ->
|
|
74
|
+
Backbone.history.navigate('#/' + chr.currentPath, { trigger: true })
|
|
75
|
+
|
|
76
|
+
showError: (response) ->
|
|
77
|
+
$container = $('#character')
|
|
78
|
+
$overlay = $('#chr_error')
|
|
79
|
+
|
|
80
|
+
if $overlay.length == 0
|
|
81
|
+
$container.after """<div id='chr_error' class='chr-error'>
|
|
82
|
+
<div id='chr_error_message' class='chr-error-message'></div>
|
|
83
|
+
<button id='chr_error_close' type='button' class='chr-error-close'>
|
|
84
|
+
<i class='chr-icon icon-close'></i>
|
|
85
|
+
</button>
|
|
86
|
+
</div>"""
|
|
87
|
+
$overlay = $('#chr_error')
|
|
88
|
+
|
|
89
|
+
entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/' }
|
|
90
|
+
escapeHtml = (string) -> String(string).replace(/[&<>"'\/]/g, (s) -> entityMap[s])
|
|
91
|
+
responseText = escapeHtml(response.responseText)
|
|
92
|
+
|
|
93
|
+
$('#chr_error_message').html "<pre>#{ responseText }</pre>"
|
|
94
|
+
$('#chr_error_close').on 'click', -> chr.execute('closeError')
|
|
95
|
+
|
|
96
|
+
$overlay.addClass('open')
|
|
97
|
+
$container.addClass('error-open')
|
|
98
|
+
|
|
99
|
+
closeError: ->
|
|
100
|
+
$('#chr_error').removeClass('open')
|
|
101
|
+
$('#character').removeClass('error-open')
|
|
102
|
+
$('#chr_error_close').off 'click'
|
|
103
|
+
|
|
104
|
+
_.map API, (method, name) => @chr.commands.setHandler(name, method)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@chr.on "before:start", (@options) -> # maps options!
|
|
108
|
+
# shortcuts
|
|
109
|
+
window.shortcuts = new window.keypress.Listener()
|
|
110
|
+
|
|
111
|
+
# close dialogs and details view on esc
|
|
112
|
+
window.shortcuts.register_combo
|
|
113
|
+
keys: 'esc'
|
|
114
|
+
is_exclusive: true
|
|
115
|
+
on_keyup: (event) ->
|
|
116
|
+
if $('#chr_images').hasClass 'open'
|
|
117
|
+
chr.execute('hideImages')
|
|
118
|
+
else if $('#chr_error').hasClass 'open'
|
|
119
|
+
chr.execute('closeError')
|
|
120
|
+
else
|
|
121
|
+
chr.execute('closeDetailsView')
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@chr.on "start", ->
|
|
125
|
+
# start history
|
|
126
|
+
if Backbone.history
|
|
127
|
+
Backbone.history.start()
|
|
128
|
+
|
|
129
|
+
# jump to first menu item when login to admin
|
|
130
|
+
if location.hash == ''
|
|
131
|
+
location.hash = $('#menu a:eq(1)').attr('href')
|
|
132
|
+
|
|
133
|
+
# disable default action for browser when drop image to window
|
|
134
|
+
$(document).bind 'drop dragover', (e) -> e.preventDefault()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
@Character.Dashboard.Charts ||= {}
|
|
2
|
+
@Character.Dashboard.Charts.visitors = (layout) ->
|
|
3
|
+
title = 'Visitors'
|
|
4
|
+
color = '#fac043'
|
|
5
|
+
|
|
6
|
+
if layout.chartType() == 'day'
|
|
7
|
+
reportModel = 'Reports-AnalyticsDaily'
|
|
8
|
+
dateFormat = (d) -> moment(d).format("MMM D")
|
|
9
|
+
else if layout.chartType() == 'week'
|
|
10
|
+
reportModel = 'Reports-AnalyticsWeekly'
|
|
11
|
+
dateFormat = (d) ->
|
|
12
|
+
weekStart = moment(d).format("MMM D")
|
|
13
|
+
weekEnd = moment(d).add('days', 6).format("MMM D")
|
|
14
|
+
"#{weekStart} - #{weekEnd}"
|
|
15
|
+
else if layout.chartType() == 'month'
|
|
16
|
+
reportModel = 'Reports-AnalyticsMonthly'
|
|
17
|
+
dateFormat = (d) -> moment(d).format("MMM YYYY")
|
|
18
|
+
|
|
19
|
+
fields = [ 'visitors' ].join(',')
|
|
20
|
+
startDate = layout.dateFrom()
|
|
21
|
+
stopDate = layout.dateTo()
|
|
22
|
+
|
|
23
|
+
url = "/admin/#{reportModel}?f=#{fields}&where__report_date=$gte:#{ startDate },$lte:#{ stopDate }&o=report_date:asc&pp=40"
|
|
24
|
+
|
|
25
|
+
$.get url, {}, (data) =>
|
|
26
|
+
d = _.map data, (r) -> { y: dateFormat(r.report_date), a: r.visitors }
|
|
27
|
+
layout.drawBarChart title, color, d
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#= require ./_visitors
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Marionette.js Layout Documentation
|
|
5
|
+
# https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.layout.md
|
|
6
|
+
#
|
|
7
|
+
@Character.Dashboard.Layout = Backbone.Marionette.LayoutView.extend
|
|
8
|
+
className: 'chr-module-generic'
|
|
9
|
+
|
|
10
|
+
template: -> """<header class="chr-details-header"><div class="title">Dashboard</div></header>
|
|
11
|
+
<div id=dashboard_view class='dashboard-view'>
|
|
12
|
+
<select id=dashboard_chart_select class='dashboard-chart-select'></select>
|
|
13
|
+
<aside class='dashboard-chart-options'>
|
|
14
|
+
<select id=dashboard_chart_type_select>
|
|
15
|
+
<option value=day>Day</option>
|
|
16
|
+
<option value=week>Week</option>
|
|
17
|
+
<option value=month>Month</option>
|
|
18
|
+
</select>
|
|
19
|
+
<input type=date id=dashboard_chart_date_from />
|
|
20
|
+
—
|
|
21
|
+
<input type=date id=dashboard_chart_date_to />
|
|
22
|
+
</aside>
|
|
23
|
+
<div id=dashboard_chart_wrapper class='dashboard-chart-wrapper'>
|
|
24
|
+
<div id=dashboard_chart></div>
|
|
25
|
+
</div>
|
|
26
|
+
<div id=dashboard_footer></div>
|
|
27
|
+
</div>"""
|
|
28
|
+
|
|
29
|
+
regions:
|
|
30
|
+
footer: '#dashboard_footer'
|
|
31
|
+
|
|
32
|
+
ui:
|
|
33
|
+
view: '#dashboard_view'
|
|
34
|
+
chart: '#dashboard_chart'
|
|
35
|
+
chartWrapper: '#dashboard_chart_wrapper'
|
|
36
|
+
chartSelect: '#dashboard_chart_select'
|
|
37
|
+
typeSelect: '#dashboard_chart_type_select'
|
|
38
|
+
dateFrom: '#dashboard_chart_date_from'
|
|
39
|
+
dateTo: '#dashboard_chart_date_to'
|
|
40
|
+
|
|
41
|
+
_renderChart: ->
|
|
42
|
+
Character.Dashboard.Charts[@chartName](@)
|
|
43
|
+
|
|
44
|
+
_selectChart: ->
|
|
45
|
+
@chartName = @ui.chartSelect.val()
|
|
46
|
+
@_renderChart()
|
|
47
|
+
|
|
48
|
+
chartType: -> @ui.typeSelect.val()
|
|
49
|
+
dateFrom: -> @ui.dateFrom.val()
|
|
50
|
+
dateTo: -> @ui.dateTo.val()
|
|
51
|
+
|
|
52
|
+
setDateRange: (fromDate, toDate) ->
|
|
53
|
+
fromDate ?= moment().subtract('month', 1).format('YYYY-MM-DD')
|
|
54
|
+
toDate ?= moment().format('YYYY-MM-DD')
|
|
55
|
+
|
|
56
|
+
@ui.dateFrom.val(fromDate)
|
|
57
|
+
@ui.dateTo.val(toDate)
|
|
58
|
+
|
|
59
|
+
_selectDateFrom: ->
|
|
60
|
+
fromDate = @dateFrom()
|
|
61
|
+
|
|
62
|
+
if @chartType() == 'day'
|
|
63
|
+
toDate = moment(fromDate).add('month', 1).format('YYYY-MM-DD')
|
|
64
|
+
else if @chartType() == 'week'
|
|
65
|
+
toDate = moment(fromDate).add('weeks', 30).format('YYYY-MM-DD')
|
|
66
|
+
else if @chartType() == 'month'
|
|
67
|
+
toDate = moment(fromDate).add('months', 30).format('YYYY-MM-DD')
|
|
68
|
+
|
|
69
|
+
@setDateRange(fromDate, toDate)
|
|
70
|
+
@_renderChart()
|
|
71
|
+
|
|
72
|
+
_selectDateTo: ->
|
|
73
|
+
toDate = @dateTo()
|
|
74
|
+
|
|
75
|
+
if @chartType() == 'day'
|
|
76
|
+
fromDate = moment(toDate).subtract('month', 1).format('YYYY-MM-DD')
|
|
77
|
+
else if @chartType() == 'week'
|
|
78
|
+
fromDate = moment(toDate).subtract('weeks', 30).format('YYYY-MM-DD')
|
|
79
|
+
else if @chartType() == 'month'
|
|
80
|
+
fromDate = moment(toDate).subtract('months', 30).format('YYYY-MM-DD')
|
|
81
|
+
|
|
82
|
+
@setDateRange(fromDate, toDate)
|
|
83
|
+
@_renderChart()
|
|
84
|
+
|
|
85
|
+
_selectType: -> @_selectDateTo()
|
|
86
|
+
|
|
87
|
+
onRender: ->
|
|
88
|
+
@currentChartType = ''
|
|
89
|
+
|
|
90
|
+
_.chain(Character.Dashboard.Charts).keys().each (key) =>
|
|
91
|
+
title = _(key).capitalize()
|
|
92
|
+
@ui.chartSelect.append("<option value=#{key}>#{title}</option>")
|
|
93
|
+
|
|
94
|
+
# TODO: after getting back to this layout from other app events declared in layout doesn't work
|
|
95
|
+
# so doing this right here:
|
|
96
|
+
@ui.chartSelect.on 'change', (e) => @_selectChart()
|
|
97
|
+
@ui.typeSelect.on 'change', (e) => @_selectType()
|
|
98
|
+
@ui.dateFrom.on 'change', (e) => @_selectDateFrom()
|
|
99
|
+
@ui.dateTo.on 'change', (e) => @_selectDateTo()
|
|
100
|
+
|
|
101
|
+
@afterRenderContent?()
|
|
102
|
+
|
|
103
|
+
onDestroy: ->
|
|
104
|
+
# NOTE: seems to be never called in current implementation
|
|
105
|
+
@ui.chartSelect.off 'change'
|
|
106
|
+
@ui.typeSelect.off 'change'
|
|
107
|
+
@ui.dateFrom.off 'change'
|
|
108
|
+
@ui.dateTo.off 'change'
|
|
109
|
+
|
|
110
|
+
@afterOnClose?()
|
|
111
|
+
|
|
112
|
+
updateScope: (@chartName, callback) ->
|
|
113
|
+
@chartName ?= 'visitors'
|
|
114
|
+
@_renderChart()
|
|
115
|
+
|
|
116
|
+
setChartTitle: (title) ->
|
|
117
|
+
@ui.chartWrapper.attr('data-title', title)
|
|
118
|
+
|
|
119
|
+
resetCurrentChart: ->
|
|
120
|
+
@ui.chart.html('')
|
|
121
|
+
@currentChart = null
|
|
122
|
+
@currentChartType = ''
|
|
123
|
+
|
|
124
|
+
drawBarChart: (title, color, data) ->
|
|
125
|
+
if data.length > 0
|
|
126
|
+
if @currentChartType == 'bar'
|
|
127
|
+
@currentChart.options.colors = [ color ]
|
|
128
|
+
@currentChart.options.labels = [ title ]
|
|
129
|
+
@currentChart.setData data
|
|
130
|
+
else
|
|
131
|
+
@resetCurrentChart()
|
|
132
|
+
|
|
133
|
+
options =
|
|
134
|
+
element: 'dashboard_chart'
|
|
135
|
+
data: data
|
|
136
|
+
barColors: [ color ]
|
|
137
|
+
labels: [ title ]
|
|
138
|
+
xkey: 'y'
|
|
139
|
+
ykeys: [ 'a' ]
|
|
140
|
+
# styles
|
|
141
|
+
hideHover: true
|
|
142
|
+
gridTextFamily: 'sans-serif'
|
|
143
|
+
gridTextColor: '#a9b1b5'
|
|
144
|
+
gridTextSize: 11
|
|
145
|
+
barOpacity: 0.6
|
|
146
|
+
barSizeRatio: 0.97
|
|
147
|
+
numLines: 6
|
|
148
|
+
gridStrokeWidth: 0.1
|
|
149
|
+
|
|
150
|
+
# https://github.com/morrisjs/morris.js
|
|
151
|
+
@currentChart = Morris.Bar options
|
|
152
|
+
|
|
153
|
+
@setChartTitle(title)
|
|
154
|
+
@currentChartType = 'bar'
|
|
155
|
+
else
|
|
156
|
+
@setChartTitle('No data')
|