character 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Character Image](http://character.s3.amazonaws.com/character1.jpg)
|
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')
|